{
  "openapi": "3.1.0",
  "info": {
    "title": "formcel API",
    "version": "v1",
    "description": "The formcel REST API lets you manage forms, submissions, folders and\nwebhook subscriptions in your workspace. Every request goes to your own\ninstance: your custom domain if one is configured, otherwise\n`{company}.formcel.com`.\n\n## Versioning\n\nThe v1 contract is frozen: fields are only ever added, never renamed or\nremoved. String fields that carry a status, role or type may gain new\nvalues over time, so treat unknown values as unknown instead of failing.\n\n## Pagination\n\nList endpoints that accept `limit` (default 25, max 100) and `offset`\n(default 0) return a page wrapped in `{\"data\": [...], \"total\", \"limit\",\n\"offset\"}`. Lists that are not paginated use the same envelope with every\nitem in `data`.\n\n## Errors\n\nErrors return a non-2xx status and a body of the form\n`{\"error\": {\"code\": \"not_found\", \"message\": \"Form not found\"}}`. Branch on\n`code`; `message` is for humans and may change.\n\n## Event delivery\n\nSubscribed events are POSTed as JSON with the headers `X-Webhook-Event`\n(the event name) and `X-Webhook-ID` (the subscription ID). Respond with\nany 2xx status within 10 seconds. Failed deliveries are retried twice,\nafter 4 and 16 seconds.\n"
  },
  "servers": [
    {
      "url": "https://{host}/api/v1",
      "variables": {
        "host": {
          "default": "{company}.formcel.com",
          "description": "Your instance host. Use your custom domain if configured, otherwise {company}.formcel.com where {company} is your workspace slug."
        }
      }
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "tags": [
    {
      "name": "Authentication",
      "description": "Verify credentials and choose the active workspace."
    },
    {
      "name": "Forms",
      "description": "Create, update, publish and organise forms."
    },
    {
      "name": "Submissions",
      "description": "Read and manage the responses collected by a form."
    },
    {
      "name": "Folders",
      "description": "Group forms into folders."
    },
    {
      "name": "Workspaces",
      "description": "List the workspaces you belong to and their members."
    },
    {
      "name": "Hooks",
      "description": "REST hook subscriptions used by automation platforms (Zapier, Make). formcel POSTs a JSON payload to the target URL whenever the subscribed event happens."
    }
  ],
  "paths": {
    "/auth/me": {
      "get": {
        "operationId": "getMe",
        "summary": "Get the authenticated user",
        "description": "Returns the user the API key belongs to. Use it to verify that a key is valid.",
        "tags": [
          "Authentication"
        ],
        "x-scope": "forms:read",
        "responses": {
          "200": {
            "description": "The authenticated user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthMe"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/auth/workspace": {
      "patch": {
        "operationId": "switchWorkspace",
        "summary": "Switch the active workspace",
        "description": "Sets the active workspace for the current session. Only session-authenticated clients keep the change; requests authenticated with an API key always act in the key owner's default workspace. Service accounts are rejected with 400 because they have no active workspace.",
        "tags": [
          "Authentication"
        ],
        "x-scope": "workspaces:manage",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkspaceSwitchRequest"
              },
              "example": {
                "workspace_id": "ws_4b1d"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The workspace that is now active.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceSummary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/forms": {
      "get": {
        "operationId": "listForms",
        "summary": "List forms",
        "description": "Returns the forms in the active workspace that the caller can access, most recently updated first. Trashed forms are excluded unless status=trashed is requested. Service accounts see every form in the instance.",
        "tags": [
          "Forms"
        ],
        "x-scope": "forms:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only return forms with this status.",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "archived",
                "disabled",
                "trashed"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "description": "Only return forms whose title contains this text.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of forms.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FormList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createForm",
        "summary": "Create a form",
        "description": "Creates a blank draft form in the active workspace. The caller becomes its owner. Requires edit access to the workspace.",
        "tags": [
          "Forms"
        ],
        "x-scope": "forms:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FormCreateRequest"
              },
              "example": {
                "title": "Customer feedback"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created form.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FormDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/forms/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/FormID"
        }
      ],
      "get": {
        "operationId": "getForm",
        "summary": "Get a form",
        "tags": [
          "Forms"
        ],
        "x-scope": "forms:read",
        "responses": {
          "200": {
            "description": "The form, including the caller's role on it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FormDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateForm",
        "summary": "Update a form",
        "description": "Updates form settings. Only the fields present in the body change. Fields that accept null are cleared when sent as null. Requires the owner or editor role.",
        "tags": [
          "Forms"
        ],
        "x-scope": "forms:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FormUpdateRequest"
              },
              "example": {
                "title": "Customer feedback 2026",
                "closes_at": "2026-12-31T23:59:59Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated form.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FormDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteForm",
        "summary": "Delete a form",
        "description": "Moves the form to the trash. Only the owner can delete a form.",
        "tags": [
          "Forms"
        ],
        "x-scope": "forms:write",
        "responses": {
          "204": {
            "description": "The form was moved to the trash."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/forms/{id}/fields": {
      "parameters": [
        {
          "$ref": "#/components/parameters/FormID"
        }
      ],
      "get": {
        "operationId": "listFormFields",
        "summary": "List form fields",
        "description": "Returns the fields of the published form in a simplified shape for mapping submission data in integrations. Not paginated.",
        "tags": [
          "Forms"
        ],
        "x-scope": "forms:read",
        "responses": {
          "200": {
            "description": "The form's fields.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FormFields"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/forms/{id}/duplicate": {
      "parameters": [
        {
          "$ref": "#/components/parameters/FormID"
        }
      ],
      "post": {
        "operationId": "duplicateForm",
        "summary": "Duplicate a form",
        "description": "Creates a copy of the form. Any role can duplicate; the caller owns the copy.",
        "tags": [
          "Forms"
        ],
        "x-scope": "forms:write",
        "responses": {
          "201": {
            "description": "The new form.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FormDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/forms/{id}/publish": {
      "parameters": [
        {
          "$ref": "#/components/parameters/FormID"
        }
      ],
      "post": {
        "operationId": "publishForm",
        "summary": "Publish a form",
        "description": "Publishes the current draft as a new immutable version. Warnings are informational; a response with warnings is still a successful publish. Requires the owner or editor role.",
        "tags": [
          "Forms"
        ],
        "x-scope": "forms:write",
        "responses": {
          "200": {
            "description": "The published version.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublishResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/forms/{id}/submissions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/FormID"
        }
      ],
      "get": {
        "operationId": "listSubmissions",
        "summary": "List submissions",
        "description": "Returns a page of the form's submissions. Submissions flagged as spam are excluded.",
        "tags": [
          "Submissions"
        ],
        "x-scope": "submissions:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only return submissions with this status.",
            "schema": {
              "$ref": "#/components/schemas/SubmissionStatus"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of submissions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubmissionList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/forms/{id}/submissions/{subId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/FormID"
        },
        {
          "$ref": "#/components/parameters/SubmissionID"
        }
      ],
      "get": {
        "operationId": "getSubmission",
        "summary": "Get a submission",
        "tags": [
          "Submissions"
        ],
        "x-scope": "submissions:read",
        "responses": {
          "200": {
            "description": "The submission.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubmissionDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteSubmission",
        "summary": "Delete a submission",
        "description": "Moves the submission to the trash and emits submission.deleted. Requires the owner or editor role.",
        "tags": [
          "Submissions"
        ],
        "x-scope": "submissions:write",
        "responses": {
          "204": {
            "description": "The submission was moved to the trash."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/forms/{id}/submissions/{subId}/status": {
      "parameters": [
        {
          "$ref": "#/components/parameters/FormID"
        },
        {
          "$ref": "#/components/parameters/SubmissionID"
        }
      ],
      "patch": {
        "operationId": "updateSubmissionStatus",
        "summary": "Update a submission's status",
        "description": "Moves the submission to another workflow status. A change is recorded in the submission's status history and emits submission.status_changed. Requires the owner or editor role.",
        "tags": [
          "Submissions"
        ],
        "x-scope": "submissions:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmissionStatusUpdateRequest"
              },
              "example": {
                "status": "approved"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated submission.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubmissionDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/folders": {
      "get": {
        "operationId": "listFolders",
        "summary": "List folders",
        "description": "Returns every folder in the active workspace that the caller can see. Not paginated.",
        "tags": [
          "Folders"
        ],
        "x-scope": "forms:read",
        "responses": {
          "200": {
            "description": "All visible folders.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FolderList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createFolder",
        "summary": "Create a folder",
        "description": "Creates a folder in the active workspace. Requires edit access to the workspace.",
        "tags": [
          "Folders"
        ],
        "x-scope": "forms:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FolderCreateRequest"
              },
              "example": {
                "label": "Marketing",
                "color": "blue"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created folder.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FolderSummary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/folders/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Folder ID.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "patch": {
        "operationId": "updateFolder",
        "summary": "Update a folder",
        "description": "Updates folder properties. Only the fields present in the body change. Send parent_id as null to move the folder to the top level. Requires the owner or editor role.",
        "tags": [
          "Folders"
        ],
        "x-scope": "forms:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FolderUpdateRequest"
              },
              "example": {
                "label": "Campaigns",
                "parent_id": null
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated folder.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FolderSummary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteFolder",
        "summary": "Delete a folder",
        "description": "Deletes the folder. Forms inside it are kept and become unfiled. Only the owner can delete a folder.",
        "tags": [
          "Folders"
        ],
        "x-scope": "forms:write",
        "responses": {
          "204": {
            "description": "The folder was deleted."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/workspaces": {
      "get": {
        "operationId": "listWorkspaces",
        "summary": "List workspaces",
        "description": "Returns the workspaces the caller is a member of. Not paginated. Service accounts are not workspace members and always receive an empty list.",
        "tags": [
          "Workspaces"
        ],
        "x-scope": "workspaces:manage",
        "responses": {
          "200": {
            "description": "The caller's workspaces.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/workspaces/{id}/members": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Workspace ID.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "listWorkspaceMembers",
        "summary": "List workspace members",
        "description": "Returns every member of the workspace. Any member can call it. Not paginated.",
        "tags": [
          "Workspaces"
        ],
        "x-scope": "workspaces:manage",
        "responses": {
          "200": {
            "description": "The workspace's members.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceMemberList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/hooks/subscribe": {
      "post": {
        "operationId": "subscribeHook",
        "summary": "Subscribe to an event",
        "description": "Registers a target URL that receives a POST whenever the event happens on the form. Subscribing again with the same form, event, source and URL returns the existing subscription.",
        "tags": [
          "Hooks"
        ],
        "x-scope": "hooks:manage",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HookSubscribeRequest"
              },
              "example": {
                "form_id": "frm_8f2c",
                "target_url": "https://hooks.zapier.com/hooks/standard/123/abc",
                "event": "submission.created",
                "source": "zapier"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The subscription.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HookSubscription"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/hooks/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Subscription ID returned by subscribeHook.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "operationId": "unsubscribeHook",
        "summary": "Unsubscribe",
        "description": "Removes a subscription created with subscribeHook. Webhooks created in the formcel app cannot be removed through this endpoint.",
        "tags": [
          "Hooks"
        ],
        "x-scope": "hooks:manage",
        "responses": {
          "204": {
            "description": "The subscription was removed."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "webhooks": {
    "submission.created": {
      "post": {
        "operationId": "submissionCreated",
        "summary": "A submission was received",
        "description": "Sent after a respondent submits the form. For forms that collect a payment it is sent once the payment completes.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmissionCreatedEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx response acknowledges the delivery."
          }
        }
      }
    },
    "submission.status_changed": {
      "post": {
        "operationId": "submissionStatusChanged",
        "summary": "A submission's status changed",
        "description": "Sent when a submission moves to another status. A bulk status change in the formcel app sends one event with submission_ids instead of submission_id and previous_status.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmissionStatusChangedEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx response acknowledges the delivery."
          }
        }
      }
    },
    "submission.deleted": {
      "post": {
        "operationId": "submissionDeleted",
        "summary": "A submission was deleted",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmissionDeletedEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx response acknowledges the delivery."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Send an API key as a Bearer token: Authorization: Bearer xf_... Create keys in Settings > API Keys; the full key is shown once. A key acts as the user who created it, with that user's access. Service account keys (xfs_...) work the same way but are limited to the scopes granted to the account; each operation lists the scope it requires."
      }
    },
    "parameters": {
      "FormID": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Form ID.",
        "schema": {
          "type": "string"
        }
      },
      "SubmissionID": {
        "name": "subId",
        "in": "path",
        "required": true,
        "description": "Submission ID.",
        "schema": {
          "type": "string"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of items to return.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        }
      },
      "Offset": {
        "name": "offset",
        "in": "query",
        "description": "Number of items to skip.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request body or parameters are invalid.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The API key is missing or invalid.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The caller lacks access to the resource, or the service account lacks the required scope.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotFound": {
        "description": "The resource does not exist.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable error code, e.g. bad_request, unauthorized, authentication_required, forbidden, insufficient_scope, not_found, payload_too_large, internal_error."
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation."
              }
            }
          }
        },
        "example": {
          "error": {
            "code": "not_found",
            "message": "Form not found"
          }
        }
      },
      "SubmissionStatus": {
        "type": "string",
        "description": "Submission workflow status. New values may be added.",
        "enum": [
          "new",
          "in_review",
          "approved",
          "rejected",
          "archived",
          "pending_payment"
        ]
      },
      "AuthMe": {
        "type": "object",
        "required": [
          "id",
          "email",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "FormSummary": {
        "type": "object",
        "required": [
          "id",
          "title",
          "status",
          "current_version",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "active, archived, disabled or trashed. New values may be added."
          },
          "current_version": {
            "type": "integer",
            "description": "Latest published version; 0 if never published."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FormDetail": {
        "type": "object",
        "required": [
          "id",
          "title",
          "status",
          "favorite",
          "current_version",
          "publish_mode",
          "created_at",
          "updated_at",
          "role"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "slug": {
            "type": "string",
            "description": "Custom URL slug. Omitted when not set."
          },
          "status": {
            "type": "string",
            "description": "active, archived, disabled or trashed. New values may be added."
          },
          "favorite": {
            "type": "boolean"
          },
          "current_version": {
            "type": "integer",
            "description": "Latest published version; 0 if never published."
          },
          "publish_mode": {
            "type": "string",
            "description": "draft (changes go live on publish) or auto (changes publish automatically)."
          },
          "folder_id": {
            "type": "string",
            "description": "Omitted when the form is unfiled."
          },
          "opens_at": {
            "type": "string",
            "format": "date-time",
            "description": "Submissions open at this time."
          },
          "closes_at": {
            "type": "string",
            "format": "date-time",
            "description": "Submissions close at this time."
          },
          "max_submissions": {
            "type": "integer",
            "description": "Submission limit."
          },
          "closed_message": {
            "type": "string",
            "description": "Shown to respondents when the form is closed."
          },
          "timezone": {
            "type": "string",
            "description": "IANA time zone used for opens_at and closes_at."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "role": {
            "type": "string",
            "description": "The caller's role on the form: owner, editor or viewer. Treat unknown values as no access."
          }
        }
      },
      "FormList": {
        "type": "object",
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FormSummary"
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        }
      },
      "FormCreateRequest": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "description": "Defaults to \"Untitled Form\" when empty."
          },
          "folder_id": {
            "type": "string",
            "description": "Folder to create the form in."
          }
        }
      },
      "FormUpdateRequest": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ],
            "description": "Custom URL slug; must be unique."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived",
              "disabled"
            ]
          },
          "favorite": {
            "type": "boolean"
          },
          "publish_mode": {
            "type": "string",
            "enum": [
              "draft",
              "auto"
            ]
          },
          "folder_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Send null to unfile the form."
          },
          "opens_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "closes_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "max_submissions": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "closed_message": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64
          }
        }
      },
      "FormField": {
        "type": "object",
        "required": [
          "key",
          "label",
          "type",
          "required"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Key of the field's value in submission data."
          },
          "label": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "description": "string, number, boolean or file. New values may be added."
          },
          "required": {
            "type": "boolean"
          }
        }
      },
      "FormFields": {
        "type": "object",
        "required": [
          "form_id",
          "fields"
        ],
        "properties": {
          "form_id": {
            "type": "string"
          },
          "fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FormField"
            }
          }
        }
      },
      "PublishResponse": {
        "type": "object",
        "required": [
          "version",
          "published_at",
          "warnings"
        ],
        "properties": {
          "version": {
            "type": "integer"
          },
          "published_at": {
            "type": "string",
            "format": "date-time"
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "SubmissionSummary": {
        "type": "object",
        "required": [
          "id",
          "form_id",
          "data",
          "schema_version",
          "status",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "form_id": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "description": "The answers keyed by field. Its shape follows the form and is not part of the API contract."
          },
          "schema_version": {
            "type": "integer",
            "description": "Form version the submission was made against."
          },
          "status": {
            "$ref": "#/components/schemas/SubmissionStatus"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SubmissionDetail": {
        "type": "object",
        "required": [
          "id",
          "form_id",
          "data",
          "schema_version",
          "status",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "form_id": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "description": "The answers keyed by field. Its shape follows the form and is not part of the API contract."
          },
          "schema_version": {
            "type": "integer",
            "description": "Form version the submission was made against."
          },
          "status": {
            "$ref": "#/components/schemas/SubmissionStatus"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SubmissionList": {
        "type": "object",
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SubmissionSummary"
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        }
      },
      "SubmissionStatusUpdateRequest": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "$ref": "#/components/schemas/SubmissionStatus"
          }
        }
      },
      "FolderSummary": {
        "type": "object",
        "required": [
          "id",
          "label",
          "icon",
          "color",
          "sort_order",
          "role",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "color": {
            "type": "string"
          },
          "parent_id": {
            "type": "string",
            "description": "Omitted for top-level folders."
          },
          "sort_order": {
            "type": "integer"
          },
          "role": {
            "type": "string",
            "description": "The caller's role on the folder. New values may be added."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FolderList": {
        "type": "object",
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FolderSummary"
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        }
      },
      "FolderCreateRequest": {
        "type": "object",
        "required": [
          "label"
        ],
        "properties": {
          "label": {
            "type": "string"
          },
          "icon": {
            "type": "string",
            "description": "Defaults to \"folder\" when empty."
          },
          "color": {
            "type": "string"
          },
          "parent_id": {
            "type": "string",
            "description": "Create the folder inside this folder."
          }
        }
      },
      "FolderUpdateRequest": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "color": {
            "type": "string"
          },
          "parent_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Send null to move the folder to the top level."
          },
          "sort_order": {
            "type": "integer"
          }
        }
      },
      "WorkspaceSummary": {
        "type": "object",
        "required": [
          "id",
          "name",
          "color",
          "role",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "color": {
            "type": "string",
            "description": "May be empty."
          },
          "role": {
            "type": "string",
            "description": "owner, admin, editor or viewer. New values may be added."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WorkspaceList": {
        "type": "object",
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkspaceSummary"
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        }
      },
      "WorkspaceMember": {
        "type": "object",
        "required": [
          "user_id",
          "name",
          "email",
          "avatar_url",
          "role",
          "created_at"
        ],
        "properties": {
          "user_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "avatar_url": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "description": "owner, admin, editor or viewer. New values may be added."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WorkspaceMemberList": {
        "type": "object",
        "required": [
          "data",
          "total",
          "limit",
          "offset"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkspaceMember"
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        }
      },
      "WorkspaceSwitchRequest": {
        "type": "object",
        "required": [
          "workspace_id"
        ],
        "properties": {
          "workspace_id": {
            "type": "string"
          }
        }
      },
      "HookSubscribeRequest": {
        "type": "object",
        "required": [
          "form_id",
          "target_url",
          "event",
          "source"
        ],
        "properties": {
          "form_id": {
            "type": "string"
          },
          "target_url": {
            "type": "string",
            "format": "uri",
            "description": "Public HTTPS URL that receives the events."
          },
          "event": {
            "type": "string",
            "enum": [
              "submission.created",
              "submission.status_changed",
              "submission.deleted",
              "submission.updated"
            ],
            "description": "submission.updated is accepted but not currently sent."
          },
          "source": {
            "type": "string",
            "enum": [
              "zapier",
              "make"
            ]
          }
        }
      },
      "HookSubscription": {
        "type": "object",
        "required": [
          "id",
          "form_id",
          "event",
          "url",
          "active",
          "source"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "form_id": {
            "type": "string"
          },
          "event": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "active": {
            "type": "boolean"
          },
          "source": {
            "type": "string"
          }
        }
      },
      "SubmissionCreatedEvent": {
        "type": "object",
        "required": [
          "event",
          "form_id",
          "data",
          "submission"
        ],
        "properties": {
          "event": {
            "type": "string",
            "const": "submission.created"
          },
          "form_id": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "description": "The answers keyed by field."
          },
          "submission": {
            "type": "object",
            "required": [
              "id",
              "form_id",
              "schema_version",
              "created_at"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "form_id": {
                "type": "string"
              },
              "schema_version": {
                "type": "integer"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "SubmissionStatusChangedEvent": {
        "type": "object",
        "required": [
          "event",
          "form_id",
          "new_status"
        ],
        "properties": {
          "event": {
            "type": "string",
            "const": "submission.status_changed"
          },
          "form_id": {
            "type": "string"
          },
          "submission_id": {
            "type": "string"
          },
          "submission_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Sent instead of submission_id for bulk changes."
          },
          "previous_status": {
            "type": "string"
          },
          "new_status": {
            "type": "string"
          }
        }
      },
      "SubmissionDeletedEvent": {
        "type": "object",
        "required": [
          "event",
          "form_id",
          "submission_id"
        ],
        "properties": {
          "event": {
            "type": "string",
            "const": "submission.deleted"
          },
          "form_id": {
            "type": "string"
          },
          "submission_id": {
            "type": "string"
          }
        }
      }
    }
  }
}

