{
  "openapi": "3.0.3",
  "info": {
    "title": "Fileinbox API",
    "version": "1.0.0",
    "description": "Fileinbox lets you programmatically manage upload pages, view submissions, download files, and send file shares. The API is versioned at /api/v1. Breaking changes are released at a new version path (e.g. /api/v2) rather than modifying this one. Human-readable docs: https://fileinbox.com/api",
    "contact": {
      "email": "christian@fileinbox.com"
    }
  },
  "servers": [
    {
      "url": "https://fileinbox.com"
    }
  ],
  "security": [
    {
      "oauth2": [
        "read"
      ]
    }
  ],
  "paths": {
    "/api/v1/ping": {
      "get": {
        "operationId": "getPing",
        "summary": "Check auth and identify the current token",
        "description": "Returns ok plus the authenticated user's id. Useful for verifying a token works.",
        "responses": {
          "200": {
            "description": "Token is valid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "ok"
                    },
                    "user_id": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createPing",
        "summary": "Check write permission",
        "description": "Requires a read+write token. Returns written: true if the token has write access.",
        "responses": {
          "200": {
            "description": "Token has write permission",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "ok"
                    },
                    "written": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/team": {
      "get": {
        "operationId": "getTeam",
        "summary": "Get the current team",
        "description": "Returns the team associated with the token (or the ?team_id= param, if the token's user belongs to multiple teams).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "Team details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Team"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/pages": {
      "get": {
        "operationId": "listPages",
        "summary": "List upload pages",
        "description": "Returns a paginated array of the current team's upload pages.",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of pages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Page"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginationMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createPage",
        "summary": "Create an upload page",
        "description": "Creates a new upload page. Requires a read+write token.",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "page": {
                    "$ref": "#/components/schemas/PageInput"
                  }
                },
                "required": [
                  "page"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Page created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Read-only token, or the team's page limit has been reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        }
      }
    },
    "/api/v1/pages/{slug}": {
      "get": {
        "operationId": "getPage",
        "summary": "Get an upload page",
        "description": "Returns a single upload page's details, including its questions and branding.",
        "security": [
          {
            "oauth2": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/PageSlug"
          }
        ],
        "responses": {
          "200": {
            "description": "Page details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updatePage",
        "summary": "Update an upload page",
        "description": "Requires a read+write token. Only include the fields you want to change.",
        "security": [
          {
            "oauth2": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/PageSlug"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "page": {
                    "$ref": "#/components/schemas/PageInput"
                  }
                },
                "required": [
                  "page"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Page updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Page"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        }
      },
      "delete": {
        "operationId": "deletePage",
        "summary": "Delete an upload page",
        "description": "Requires a read+write token.",
        "security": [
          {
            "oauth2": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/PageSlug"
          }
        ],
        "responses": {
          "204": {
            "description": "Page deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/pages/{slug}/responses": {
      "get": {
        "operationId": "listResponses",
        "summary": "List a page's responses",
        "description": "Returns a paginated array of submissions made to the page.",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/PageSlug"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Only return responses created at or after this ISO 8601 timestamp.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of responses",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Response"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginationMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/pages/{slug}/responses/{id}": {
      "get": {
        "operationId": "getResponse",
        "summary": "Get a response",
        "description": "Returns a single response's answers, files, and metadata.",
        "security": [
          {
            "oauth2": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/PageSlug"
          },
          {
            "$ref": "#/components/parameters/ResponseId"
          }
        ],
        "responses": {
          "200": {
            "description": "Response details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Response"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteResponse",
        "summary": "Delete a response",
        "description": "Requires a read+write token. Soft-deletes the response.",
        "security": [
          {
            "oauth2": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/PageSlug"
          },
          {
            "$ref": "#/components/parameters/ResponseId"
          }
        ],
        "responses": {
          "204": {
            "description": "Response deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/pages/{slug}/responses/{response_id}/files/{id}": {
      "get": {
        "operationId": "downloadResponseFile",
        "summary": "Download a response file",
        "description": "Redirects (302) to a signed download URL that expires in 5 minutes. Follow the redirect to download the file.",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/PageSlug"
          },
          {
            "name": "response_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Response id"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Response file id"
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to a signed, time-limited download URL"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/pages/{slug}/submissions": {
      "post": {
        "operationId": "createSubmission",
        "summary": "Submit answers and files to a page (public)",
        "description": "Unauthenticated endpoint. Only works if the page has api_submissions_enabled set to true. Accepts either multipart/form-data (answers + files together) or application/json (answers with signed_id file references from the two-step upload flow). Rate limit: 10 requests/minute per IP.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSlug"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/SubmissionInput"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmissionInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Submission created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid access code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "API submissions are not enabled for this page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/pages/{slug}/uploads": {
      "post": {
        "operationId": "createPageUploadUrls",
        "summary": "Request presigned upload URLs for a submission (public)",
        "description": "Step 1 of the direct-upload flow for large or browser-uploaded files. Unauthenticated; only works if the page has api_submissions_enabled set to true. Upload each file's bytes with PUT to the returned direct_upload.url, then submit signed_ids via POST /pages/{slug}/submissions. Rate limit: 20 requests/minute per IP.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSlug"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "blobs": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/BlobInput"
                    }
                  }
                },
                "required": [
                  "blobs"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Presigned upload URLs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DirectUploadResult"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "API submissions are not enabled for this page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/shares": {
      "get": {
        "operationId": "listShares",
        "summary": "List file shares",
        "description": "Returns a paginated array of the current team's file shares.",
        "security": [
          {
            "oauth2": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of shares",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ShareSummary"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginationMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createShare",
        "summary": "Create a file share",
        "description": "Finalizes a share from files already uploaded via POST /share_uploads. Requires a read+write token.",
        "security": [
          {
            "oauth2": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShareInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Share created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShareDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Free plan share limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        }
      }
    },
    "/api/v1/shares/{id}": {
      "get": {
        "operationId": "getShare",
        "summary": "Get a share",
        "description": "Returns share details plus _links and _actions for hypermedia navigation.",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/ShareCode"
          }
        ],
        "responses": {
          "200": {
            "description": "Share details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShareDetail"
                    },
                    "_links": {
                      "type": "object"
                    },
                    "_actions": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateShare",
        "summary": "Update a share",
        "description": "Requires a read+write token.",
        "security": [
          {
            "oauth2": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/ShareCode"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Share updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShareDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          }
        }
      },
      "delete": {
        "operationId": "deleteShare",
        "summary": "Delete a share",
        "description": "Requires a read+write token. Soft-deletes the share.",
        "security": [
          {
            "oauth2": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/ShareCode"
          }
        ],
        "responses": {
          "204": {
            "description": "Share deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/share_uploads": {
      "post": {
        "operationId": "createShareUploadUrls",
        "summary": "Request presigned upload URLs for a share",
        "description": "Step 1 of the file-sharing flow. Requires a read+write token. Upload each file's bytes with PUT to the returned direct_upload.url, then finalize with POST /shares using the returned signed_ids.",
        "security": [
          {
            "oauth2": [
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "blobs": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/BlobInput"
                    }
                  }
                },
                "required": [
                  "blobs"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Presigned upload URLs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DirectUploadResult"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Free plan share limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "A file exceeds the plan's max share file size",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API token from https://fileinbox.com/settings/api_tokens, sent as `Authorization: Bearer fi_xxxxx`. Prefer the oauth2 scheme below for scope names."
      },
      "oauth2": {
        "type": "oauth2",
        "description": "Not a live OAuth flow. Fileinbox issues bearer tokens directly at https://fileinbox.com/settings/api_tokens rather than via an authorization server; this scheme documents the `read` and `write` permission levels those tokens carry, since OAS3 HTTP bearer schemes can't declare scopes.",
        "flows": {
          "clientCredentials": {
            "tokenUrl": "https://fileinbox.com/settings/api_tokens",
            "scopes": {
              "read": "View pages, responses, shares, and teams.",
              "write": "Create, update, and delete pages, responses, and shares. Includes read access."
            }
          }
        }
      }
    },
    "parameters": {
      "TeamId": {
        "name": "team_id",
        "in": "query",
        "required": false,
        "description": "Team id. Defaults to the token owner's default team.",
        "schema": {
          "type": "integer"
        }
      },
      "Page": {
        "name": "page",
        "in": "query",
        "required": false,
        "description": "Page number, 1-indexed.",
        "schema": {
          "type": "integer",
          "default": 1
        }
      },
      "PerPage": {
        "name": "per_page",
        "in": "query",
        "required": false,
        "description": "Results per page (max 100).",
        "schema": {
          "type": "integer",
          "default": 25,
          "maximum": 100
        }
      },
      "PageSlug": {
        "name": "slug",
        "in": "path",
        "required": true,
        "description": "Page slug.",
        "schema": {
          "type": "string"
        }
      },
      "ResponseId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Response id.",
        "schema": {
          "type": "integer"
        }
      },
      "ShareCode": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Share code.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid token",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Token doesn't have required permission",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "ValidationFailed": {
        "description": "Validation error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded (100 requests/minute per token)",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds until the rate limit window resets."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorEnvelope": {
        "type": "object",
        "description": "Shared error shape returned by every endpoint in this API.",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "example": "validation_failed"
              },
              "message": {
                "type": "string",
                "example": "Slug has already been taken"
              },
              "details": {
                "type": "object",
                "nullable": true,
                "description": "Present for 422 responses; maps field names to error messages."
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "PaginationMeta": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer"
          },
          "per_page": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          }
        }
      },
      "Team": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "plan": {
            "type": "string",
            "example": "fibasic-yearly"
          },
          "pages_count": {
            "type": "integer"
          },
          "responses_count": {
            "type": "integer"
          }
        }
      },
      "Question": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "textarea",
              "email",
              "select",
              "files"
            ]
          },
          "title": {
            "type": "string"
          },
          "required": {
            "type": "boolean"
          },
          "placeholder": {
            "type": "string"
          },
          "help": {
            "type": "string"
          },
          "settings": {
            "type": "object"
          }
        }
      },
      "Page": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "responses_count": {
            "type": "integer"
          },
          "questions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Question"
            }
          },
          "branding": {
            "type": "object"
          },
          "access_code_enabled": {
            "type": "boolean"
          },
          "api_submissions_enabled": {
            "type": "boolean"
          }
        }
      },
      "PageInput": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "URL slug (unique, letters/numbers/hyphens)."
          },
          "title": {
            "type": "string"
          },
          "access_code": {
            "type": "string"
          },
          "api_submissions_enabled": {
            "type": "boolean"
          },
          "branding": {
            "type": "object"
          },
          "new_questions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Question"
            }
          }
        }
      },
      "Answer": {
        "type": "object",
        "properties": {
          "question_title": {
            "type": "string"
          },
          "question_type": {
            "type": "string"
          },
          "value": {}
        }
      },
      "ResponseFile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "filename": {
            "type": "string"
          },
          "content_type": {
            "type": "string"
          },
          "byte_size": {
            "type": "integer"
          },
          "download_url": {
            "type": "string",
            "description": "Omitted (with gated: true on the response) if files are gated by plan/age limits."
          }
        }
      },
      "Response": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "answers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Answer"
            }
          },
          "ip": {
            "type": "string"
          },
          "location": {
            "type": "string"
          },
          "client": {
            "type": "string"
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResponseFile"
            }
          },
          "gated": {
            "type": "boolean",
            "description": "True if this response's files are gated (free plan, older than 30 days)."
          }
        }
      },
      "SubmissionInput": {
        "type": "object",
        "properties": {
          "answers": {
            "type": "object",
            "description": "Map of question id to answer value."
          },
          "files": {
            "type": "object",
            "description": "Map of file-question id to an array of files (multipart) or {signed_id, name} objects (JSON)."
          },
          "access_code": {
            "type": "string",
            "description": "Required if the page has an access code set."
          }
        },
        "required": [
          "answers"
        ]
      },
      "BlobInput": {
        "type": "object",
        "properties": {
          "filename": {
            "type": "string"
          },
          "byte_size": {
            "type": "integer"
          },
          "content_type": {
            "type": "string"
          },
          "checksum": {
            "type": "string",
            "description": "Base64 MD5 checksum. Optional; a placeholder is generated if omitted."
          }
        },
        "required": [
          "filename",
          "byte_size",
          "content_type"
        ]
      },
      "DirectUploadResult": {
        "type": "object",
        "properties": {
          "signed_id": {
            "type": "string"
          },
          "filename": {
            "type": "string"
          },
          "content_type": {
            "type": "string"
          },
          "byte_size": {
            "type": "integer"
          },
          "direct_upload": {
            "type": "object",
            "properties": {
              "url": {
                "type": "string",
                "format": "uri",
                "description": "PUT the file bytes here."
              },
              "headers": {
                "type": "object",
                "description": "Headers to send with the PUT request."
              }
            }
          }
        }
      },
      "ShareFile": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          },
          "content_type": {
            "type": "string"
          }
        }
      },
      "ShareSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Share code."
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "files_count": {
            "type": "integer"
          },
          "download_count": {
            "type": "integer"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ShareDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Share code."
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ShareFile"
            }
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "email"
            }
          },
          "password_protected": {
            "type": "boolean"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "download_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ShareInput": {
        "type": "object",
        "properties": {
          "signed_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "signed_id values returned from POST /share_uploads."
          },
          "title": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "email"
            }
          },
          "password": {
            "type": "string"
          },
          "expires_in": {
            "type": "string",
            "description": "e.g. \"7d\", or \"never\". Free plans may have a forced expiry."
          }
        },
        "required": [
          "signed_ids"
        ]
      }
    }
  }
}
