openapi: 3.0.3
info:
  title: FormWork API
  description: |
    FormWork REST API for form building, entries, workflows, and data tables.

    ## Authentication

    All API endpoints require authentication using Bearer tokens (API keys).

    ### Getting an API Key

    1. Log in to the FormWork web interface
    2. Navigate to Settings → API Keys
    3. Click "Create API Key" and give it a name
    4. Copy the key immediately (it's shown only once!)

    ### Using Your API Key

    Include your API key in the `Authorization` header of every request:

    ```
    Authorization: Bearer fwk_live_abc123...
    ```

    ### Key Format

    API keys follow this format: `fwk_live_<64_hex_characters>`

  version: 1.0.0

tags:
  - name: System
    description: Health checks and API documentation
  - name: Accounts
    description: Manage accounts
  - name: Users
    description: User management and account operations
  - name: Projects
    description: Project management operations
  - name: Forms
    description: Form creation and management
  - name: Form Versions
    description: Form version control and publishing
  - name: Entries
    description: Form entry management and submission
  - name: Data Tables
    description: Data table and row operations
  - name: Workflows
    description: Workflow definitions and execution
  - name: References
    description: Reference resolution for dynamic fields
  - name: Client
    description: Public form client API for rendering and submitting forms
  - name: Files
    description: File upload and management operations
  - name: Jobs
    description: Background job management

security:
  - bearerAuth: []

paths:
  /v1/client/form_schema:
    get:
      summary: Get form schema without creating an entry
      description: |
        Returns the form schema (form version) without creating an entry.
        Useful for rendering the form before user interaction.

        The response also includes initial default answers, field visibility, and
        validation errors computed as if a new entry had been created, but without
        persisting anything. This allows clients to render default values and
        hide/show fields correctly before the first answer update.
      operationId: getClientFormSchema
      tags:
        - Client
      security: [] # No auth required
      parameters:
        - name: form_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: Form ID
        - name: preview_version_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional draft version to get schema for
      responses:
        '200':
          description: Form schema retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientFormSchema'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/client/entries:
    post:
      summary: Create a new entry
      description: |
        Creates a new, blank entry for a form. 
        **This is the only time the `entry_key` is returned.**
        The client must store this key for all subsequent operations on this entry.
      operationId: createClientEntry
      tags:
        - Client
      security: [] # No auth required
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - form_id
              properties:
                form_id:
                  type: string
                  format: uuid
                  description: Form ID
                preview_version_id:
                  type: string
                  format: uuid
                  description: Optional draft version to create the entry against
      responses:
        '201':
          description: Entry created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientEntry'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/client/entries/{entryId}:
    parameters:
      - name: entryId
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: Entry ID
    get:
      summary: Get entry state
      description: Retrieves the current state of an existing entry
      operationId: getClientEntry
      tags:
        - Client
      security:
        - entryKeyAuth: []
      responses:
        '200':
          description: Current entry state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientEntry'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Update entry answers
      description: |
        Updates one or more field answers. Returns the full, re-evaluated state of the entry
        including updated validation errors and field visibility.
      operationId: updateClientEntry
      tags:
        - Client
      security:
        - entryKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                answers:
                  type: object
                  description: Field values to update in flat path notation
                  additionalProperties: true
                  example:
                    'personal_info.first_name': 'Jane'
                    'some_group_repeater[instance123].name': 'John'
              required:
                - answers
      responses:
        '200':
          description: Entry updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientEntry'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/client/entries/{entryId}/repeaters:
    parameters:
      - name: entryId
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: Entry ID
    post:
      summary: Add repeater instance
      description: Adds a new item to a repeatable field
      operationId: addRepeaterInstance
      tags:
        - Client
      security:
        - entryKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                field_path:
                  type: string
                  description: The base path of the repeatable field
                  example: 'some_group_repeater'
              required:
                - field_path
      responses:
        '200':
          description: Repeater instance added successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientEntry'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/client/entries/{entryId}/repeaters/{instancePath}:
    parameters:
      - name: entryId
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: Entry ID
      - name: instancePath
        in: path
        required: true
        schema:
          type: string
        description: The full path to the instance (URL-encoded), e.g., tags[instance123]
    delete:
      summary: Delete repeater instance
      description: Deletes an item from a repeatable field. The path should be URL-encoded.
      operationId: deleteRepeaterInstance
      tags:
        - Client
      security:
        - entryKeyAuth: []
      responses:
        '200':
          description: Repeater instance deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientEntry'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/client/entries/{entryId}/submit:
    parameters:
      - name: entryId
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: Entry ID
    post:
      summary: Submit entry
      description: Submits the entry for processing
      operationId: submitClientEntry
      tags:
        - Client
      security:
        - entryKeyAuth: []
      responses:
        '200':
          description: Entry submitted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientEntry'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/client/entries/{entryId}/button-click:
    parameters:
      - name: entryId
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: Entry ID
    post:
      summary: Handle button field click
      description: |
        Records a button field click event for an entry.
        This can trigger workflows that have a button_clicked trigger configured for the specified field.
        Returns the updated entry state after any workflows have executed.
      operationId: buttonClick
      tags:
        - Client
      security:
        - entryKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                field_id:
                  type: string
                  description: The ID of the button field that was clicked
                  example: 'submit_button'
              required:
                - field_id
      responses:
        '200':
          description: Button click processed successfully, returns updated entry state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientEntry'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/address/autocomplete:
    post:
      summary: Address autocomplete
      description: |
        Returns address suggestions based on partial input text.
        Used by address fields with lookup enabled.
      operationId: addressAutocomplete
      tags:
        - Client
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                query:
                  type: string
                  description: Partial address text to search for
                  example: '10 downing'
                provider:
                  type: string
                  description: Geocoding provider to use
                  enum: [here]
                  default: here
                countries:
                  type: array
                  items:
                    type: string
                  description: ISO 3166-1 alpha-3 country codes to restrict search
                  example: ['GBR', 'USA']
                language:
                  type: string
                  description: Language code for results
                  example: 'en-GB'
                limit:
                  type: integer
                  description: Maximum number of suggestions to return
                  default: 5
                  minimum: 1
                  maximum: 20
              required:
                - query
      responses:
        '200':
          description: Address suggestions
          content:
            application/json:
              schema:
                type: object
                properties:
                  suggestions:
                    type: array
                    items:
                      $ref: '#/components/schemas/AddressSuggestion'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v1/health:
    get:
      summary: Get health status
      operationId: getHealth
      tags:
        - System
      security: [] # No auth required
      responses:
        '200':
          description: Health status
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: healthy

  /v1/openapi.yaml:
    get:
      summary: Get OpenAPI specification in YAML format
      operationId: getOpenAPIYaml
      tags:
        - System
      security: [] # No auth required
      responses:
        '200':
          description: OpenAPI specification in YAML format
          content:
            application/yaml:
              schema:
                type: string

  /v1/openapi.json:
    get:
      summary: Get OpenAPI specification in JSON format
      operationId: getOpenAPIJson
      tags:
        - System
      security: [] # No auth required
      responses:
        '200':
          description: OpenAPI specification in JSON format
          content:
            application/json:
              schema:
                type: object

  /v1/keys:
    get:
      summary: List API keys
      description: Returns all API keys for the authenticated account
      operationId: listAPIKeys
      tags:
        - Accounts
      responses:
        '200':
          description: List of API keys
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/APIKey'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      summary: Create a new API key
      description: |
        Creates a new API key for the authenticated account.

        **Important:** The full API key is returned only once in the response.
        Store it securely as it cannot be retrieved again.
      operationId: createAPIKey
      tags:
        - Accounts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/APIKeyCreate'
      responses:
        '201':
          description: API key created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIKeyCreateResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/keys/{keyId}:
    delete:
      summary: Revoke an API key
      description: Revokes an API key, making it immediately unusable
      operationId: revokeAPIKey
      tags:
        - Accounts
      parameters:
        - name: keyId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: API key revoked
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/accounts/{accountId}:
    parameters:
      - $ref: '#/components/parameters/AccountId'
    get:
      summary: Get account details
      description: Returns details for the specified account
      operationId: getAccount
      tags:
        - Accounts
      responses:
        '200':
          description: Account details
          content:
            application/json:
              schema:
                type: object
                properties:
                  account:
                    $ref: '#/components/schemas/Account'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Update account
      description: Updates the account name
      operationId: updateAccount
      tags:
        - Accounts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AccountUpdate'
      responses:
        '200':
          description: Account updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  account:
                    $ref: '#/components/schemas/Account'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/accounts/{accountId}/users:
    parameters:
      - $ref: '#/components/parameters/AccountId'
    get:
      summary: List account users
      description: Returns all users in the specified account
      operationId: listAccountUsers
      tags:
        - Accounts
      responses:
        '200':
          description: List of account users
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AccountUserDetails'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      summary: Invite user to account
      description: Sends an invitation to a user to join the account
      operationId: inviteUser
      tags:
        - Accounts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                role:
                  type: string
                  enum: [admin, standard]
                  default: standard
              required:
                - email
      responses:
        '204':
          description: Invitation sent
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/accounts/{accountId}/users/{userId}/role:
    parameters:
      - $ref: '#/components/parameters/AccountId'
      - $ref: '#/components/parameters/UserId'
    patch:
      summary: Update user role
      description: Updates the role of a user within the account
      operationId: updateUserRole
      tags:
        - Accounts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                role:
                  type: string
                  enum: [admin, standard]
              required:
                - role
      responses:
        '204':
          description: User role updated
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/accounts/{accountId}/users/{userId}:
    parameters:
      - $ref: '#/components/parameters/AccountId'
      - $ref: '#/components/parameters/UserId'
    delete:
      summary: Remove user from account
      description: Removes a user from the account
      operationId: removeUser
      tags:
        - Accounts
      responses:
        '204':
          description: User removed
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/users/{userId}/password:
    parameters:
      - $ref: '#/components/parameters/UserId'
    patch:
      summary: Change password
      description: Changes the password for the authenticated user
      operationId: changePassword
      tags:
        - Users
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                current_password:
                  type: string
                  format: password
                new_password:
                  type: string
                  format: password
                  minLength: 8
              required:
                - current_password
                - new_password
      responses:
        '204':
          description: Password changed successfully
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/users/{userId}/accounts:
    parameters:
      - $ref: '#/components/parameters/UserId'
    get:
      summary: Get user's accounts
      description: Returns all accounts the user has access to
      operationId: getUserAccounts
      tags:
        - Users
      responses:
        '200':
          description: List of user accounts
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AccountUser'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Forbidden - user can only access their own accounts
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v1/accounts:
    post:
      summary: Create new account
      description: Creates a new account for the authenticated user
      operationId: createAccount
      tags:
        - Accounts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AccountCreate'
      responses:
        '201':
          description: Account created
          content:
            application/json:
              schema:
                type: object
                properties:
                  account:
                    $ref: '#/components/schemas/Account'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/auth/session/switch-account:
    post:
      summary: Switch active account
      description: Switches the active account for the current session
      operationId: switchAccount
      tags:
        - Accounts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                account_id:
                  type: string
                  format: uuid
              required:
                - account_id
      responses:
        '204':
          description: Account switched successfully
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Forbidden - user does not have access to this account
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v1/projects:
    get:
      summary: List all projects
      description: Returns all projects for the authenticated account
      operationId: listProjects
      tags:
        - Projects
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of projects
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Project'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      summary: Create a new project
      description: Creates a new project within the authenticated account
      operationId: createProject
      tags:
        - Projects
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectCreate'
      responses:
        '201':
          description: Project created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/projects/{projectId}:
    parameters:
      - $ref: '#/components/parameters/ProjectId'
    get:
      summary: Get a project by ID
      description: Returns a single project by its ID
      operationId: getProject
      tags:
        - Projects
      responses:
        '200':
          description: Project details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Update a project
      description: Updates a project's details
      operationId: updateProject
      tags:
        - Projects
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectUpdate'
      responses:
        '200':
          description: Project updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      summary: Delete a project
      description: Deletes a project and all its associated resources
      operationId: deleteProject
      tags:
        - Projects
      responses:
        '204':
          description: Project deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/users:
    get:
      summary: List all users
      description: Returns all users in the authenticated account
      operationId: listUsers
      tags:
        - Users
      responses:
        '200':
          description: List of users
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AccountUserDetails'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/users/{userId}:
    parameters:
      - $ref: '#/components/parameters/UserId'
    get:
      summary: Get a user by ID
      description: Returns a single user's details
      operationId: getUser
      tags:
        - Users
      responses:
        '200':
          description: User details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountUserDetails'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/forms:
    get:
      summary: List all forms in a project
      operationId: listForms
      tags:
        - Forms
      parameters:
        - name: project_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: Project ID
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of forms
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/FormSummary'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
    post:
      summary: Create a new form
      operationId: createForm
      tags:
        - Forms
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FormCreate'
      responses:
        '201':
          description: Form created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Form'
        '400':
          $ref: '#/components/responses/BadRequest'

  /v1/forms/{formId}:
    parameters:
      - $ref: '#/components/parameters/FormId'
    get:
      summary: Get form by ID
      operationId: getForm
      tags:
        - Forms
      responses:
        '200':
          description: Form details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Form'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Update form
      operationId: updateForm
      tags:
        - Forms
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FormUpdate'
      responses:
        '200':
          description: Form updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Form'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      summary: Delete form
      operationId: deleteForm
      tags:
        - Forms
      responses:
        '204':
          description: Form deleted
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/form_versions:
    get:
      summary: List all versions of a form
      operationId: listFormVersions
      tags:
        - Form Versions
      parameters:
        - name: form_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: Form ID
      responses:
        '200':
          description: List of form versions
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/FormVersionSummary'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      summary: Create a new form version
      operationId: createFormVersion
      tags:
        - Form Versions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FormVersionCreate'
      responses:
        '201':
          description: Form version created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormVersion'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/form_versions/{versionId}:
    parameters:
      - $ref: '#/components/parameters/VersionId'
    get:
      summary: Get form version by ID
      operationId: getFormVersion
      tags:
        - Form Versions
      responses:
        '200':
          description: Form version details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormVersion'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      summary: Update form version
      operationId: updateFormVersion
      tags:
        - Form Versions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FormVersionUpdate'
      responses:
        '200':
          description: Form version updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormVersion'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Patch form version settings
      description: Update specific form version settings without requiring pages/workflows
      operationId: patchFormVersion
      tags:
        - Form Versions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FormVersionPatch'
      responses:
        '200':
          description: Form version settings updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormVersion'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      summary: Delete form version
      operationId: deleteFormVersion
      tags:
        - Form Versions
      responses:
        '204':
          description: Form version deleted
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/form_versions/{versionId}/publish:
    parameters:
      - $ref: '#/components/parameters/VersionId'
    post:
      summary: Publish a form version
      operationId: publishFormVersion
      tags:
        - Form Versions
      responses:
        '200':
          description: Form version published
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormVersion'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/form_versions/{versionId}/copy_to_draft:
    parameters:
      - $ref: '#/components/parameters/VersionId'
    post:
      summary: Copy a version to a new draft
      operationId: copyVersionToDraft
      tags:
        - Form Versions
      responses:
        '201':
          description: New draft version created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormVersion'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/form_versions/{versionId}/discard_draft:
    parameters:
      - $ref: '#/components/parameters/VersionId'
    post:
      summary: Discard a draft version
      operationId: discardDraftVersion
      tags:
        - Form Versions
      responses:
        '204':
          description: Draft version discarded
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/form_versions/{versionId}/fields:
    parameters:
      - $ref: '#/components/parameters/VersionId'
    post:
      summary: Create a new field
      operationId: createField
      tags:
        - Form Builder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - type
                - label
                - position
              properties:
                id:
                  type: string
                  description: Optional field ID. Must be lowercase alphanumeric with underscores only, starting with a letter. If not provided, a short random ID is generated.
                  pattern: '^[a-z][a-z0-9_]*$'
                type:
                  type: string
                  description: Field type
                  enum:
                    [
                      text,
                      email,
                      url,
                      textarea,
                      hidden,
                      number,
                      date,
                      time,
                      datetime,
                      toggle,
                      select,
                      radio,
                      slider,
                      phone,
                      password,
                      rating,
                      signature,
                      file,
                      name,
                      address,
                      group,
                      heading,
                      paragraph,
                      button,
                      html,
                      image,
                      video,
                    ]
                label:
                  type: string
                  description: Field label
                position:
                  type: object
                  description: Where to insert the field
                  properties:
                    after_field_id:
                      type: string
                    page_id:
                      type: string
                    inside:
                      type: boolean
                      default: false
                config:
                  type: object
                  additionalProperties: true
      responses:
        '201':
          description: Field created
          content:
            application/json:
              schema:
                type: object
                properties:
                  field:
                    $ref: '#/components/schemas/Field'
                  pages:
                    type: array
                    items:
                      $ref: '#/components/schemas/Page'
                  version:
                    type: integer
        '400':
          $ref: '#/components/responses/BadRequest'

  /v1/form_versions/{versionId}/fields/{fieldId}:
    parameters:
      - $ref: '#/components/parameters/VersionId'
      - name: fieldId
        in: path
        required: true
        schema:
          type: string
    put:
      summary: Update field (full replacement)
      description: |
        Replaces a field with the provided field object.
        Send the complete field object with all properties.
        The entire field structure is validated after replacement.

        This is simpler and more reliable than partial updates - just send
        the full field with your changes.
      operationId: updateField
      tags:
        - Form Builder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Field'
      responses:
        '200':
          description: Field updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  field:
                    $ref: '#/components/schemas/Field'
                  pages:
                    type: array
                    items:
                      $ref: '#/components/schemas/Page'
                  version:
                    type: integer
        '400':
          $ref: '#/components/responses/BadRequest'
    delete:
      summary: Delete a field
      operationId: deleteField
      tags:
        - Form Builder
      responses:
        '200':
          description: Field deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  pages:
                    type: array
                    items:
                      $ref: '#/components/schemas/Page'
                  version:
                    type: integer
        '400':
          $ref: '#/components/responses/BadRequest'

  /v1/form_versions/{versionId}/fields/{fieldId}/move:
    parameters:
      - $ref: '#/components/parameters/VersionId'
      - name: fieldId
        in: path
        required: true
        schema:
          type: string
    post:
      summary: Move a field
      operationId: moveField
      tags:
        - Form Builder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                direction:
                  type: string
                  enum: [up, down]
                after_field_id:
                  type: string
      responses:
        '200':
          description: Field moved
          content:
            application/json:
              schema:
                type: object
                properties:
                  pages:
                    type: array
                    items:
                      $ref: '#/components/schemas/Page'
                  version:
                    type: integer

  /v1/entries:
    get:
      summary: List form entries
      operationId: listFormEntries
      tags:
        - Entries
      parameters:
        - name: form_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: Form ID
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of form entries
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/EntrySummary'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      summary: Create a new form entry
      description: Create a new entry with draft status and default answers
      operationId: createFormEntry
      tags:
        - Entries
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                form_id:
                  type: string
                  format: uuid
                  description: Form ID
              required:
                - form_id
      responses:
        '201':
          description: Entry created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Entry'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/entries/{entryId}:
    parameters:
      - $ref: '#/components/parameters/EntryId'
    get:
      summary: Get entry by ID
      operationId: getFormEntry
      tags:
        - Entries
      responses:
        '200':
          description: Entry details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Entry'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Update entry metadata
      description: Update entry-level fields like status, not answers or metafields
      operationId: updateEntry
      tags:
        - Entries
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                status:
                  type: string
                  enum: [incomplete, complete, submitted]
                  description: Entry status
      responses:
        '200':
          description: Entry updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Entry'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/entries/{entryId}/answers:
    parameters:
      - $ref: '#/components/parameters/EntryId'
    patch:
      summary: Update entry answers
      description: Update answer field values, trigger workflows, validate, and recalculate visibility
      operationId: updateEntryAnswers
      tags:
        - Entries
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AnswerUpdate'
      responses:
        '200':
          description: Answers updated with changes
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnswerUpdateResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/entries/{entryId}/metafields:
    parameters:
      - $ref: '#/components/parameters/EntryId'
    patch:
      summary: Update entry metafields
      description: Update metafields (internal operational data) for an entry. This resolves the metafields form/entry and updates its answers.
      operationId: updateEntryMetafields
      tags:
        - Entries
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AnswerUpdate'
      responses:
        '200':
          description: Metafields updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnswerUpdateResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/entries/{entryId}/admin_edit_start:
    parameters:
      - $ref: '#/components/parameters/EntryId'
    post:
      summary: Start admin edit session
      description: |
        Marks the beginning of an admin edit session for a completed entry.
        Sets admin_editing_started_at/admin_editing_started_by so other users can see who is editing.
        No revision is created yet.
      operationId: adminEditEntryStart
      tags:
        - Entries
      responses:
        '200':
          description: Admin edit session started
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Entry'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/entries/{entryId}/admin_edit_complete:
    parameters:
      - $ref: '#/components/parameters/EntryId'
    post:
      summary: Complete admin edit session (creates revision)
      description: |
        Creates a new revision and marks the entry as not being in admin edit mode.
        Uses the latest existing revision snapshot as the baseline for the diff and
        snapshots the current entry answers.
      operationId: adminEditEntryComplete
      tags:
        - Entries
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
                  description: Optional reason for the edit
      responses:
        '200':
          description: Entry updated with revision created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Entry'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/entries/{entryId}/admin_edit_discard:
    parameters:
      - $ref: '#/components/parameters/EntryId'
    post:
      summary: Discard admin edit session
      description: |
        Discards an in-progress admin edit session and restores the entry answers
        from the latest revision snapshot. No new revision is created.
        Also clears admin_editing_started_at/admin_editing_started_by.
      operationId: adminEditEntryDiscard
      tags:
        - Entries
      responses:
        '200':
          description: Entry restored to last saved revision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Entry'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/entry_revisions:
    get:
      summary: List entry revisions
      description: Returns all revisions for a specific entry
      operationId: listEntryRevisions
      tags:
        - Entries
      parameters:
        - name: entry_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: Entry ID to get revisions for
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of revisions
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/EntryRevision'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '400':
          $ref: '#/components/responses/BadRequest'

  /v1/entry_revisions/{revisionId}:
    parameters:
      - name: revisionId
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: Revision ID
    get:
      summary: Get specific revision
      description: Returns details for a specific entry revision
      operationId: getEntryRevision
      tags:
        - Entries
      responses:
        '200':
          description: Revision details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EntryRevision'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/entries/{entryId}/files/prepare:
    parameters:
      - name: entryId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      summary: Prepare file upload
      description: Request a presigned URL for uploading a file to an entry
      operationId: prepareClientFileUpload
      tags:
        - Files
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - filename
                - size
                - mime_type
              properties:
                filename:
                  type: string
                  description: Original filename
                size:
                  type: integer
                  format: int64
                  description: File size in bytes
                mime_type:
                  type: string
                  description: MIME type of the file
                checksum:
                  type: string
                  description: SHA-256 checksum of the file (optional)
      responses:
        '200':
          description: Presigned upload URL generated
          content:
            application/json:
              schema:
                type: object
                required:
                  - file_id
                  - upload_url
                  - upload_method
                  - expires_at
                properties:
                  file_id:
                    type: string
                    format: uuid
                  upload_url:
                    type: string
                    description: Presigned S3 URL for upload
                  upload_method:
                    type: string
                    description: HTTP method to use (typically PUT)
                  upload_headers:
                    type: object
                    additionalProperties:
                      type: string
                    description: Headers to include in upload request
                  expires_at:
                    type: string
                    format: date-time
                    description: When the presigned URL expires
        '400':
          $ref: '#/components/responses/BadRequest'

  /v1/entries/{entryId}/files/{fileId}/complete:
    parameters:
      - name: entryId
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: fileId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      summary: Complete file upload
      description: Confirm that file upload to S3 has completed successfully
      operationId: completeClientFileUpload
      tags:
        - Files
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                checksum:
                  type: string
                  description: SHA-256 checksum for verification
      responses:
        '200':
          description: File upload completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/File'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/files/prepare:
    post:
      summary: Prepare file upload
      description: Request a presigned URL for uploading a file with any context (admin_import, export, etc.)
      operationId: prepareFileUpload
      tags:
        - Files
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - filename
                - size
                - mime_type
                - context
                - context_id
              properties:
                filename:
                  type: string
                  description: Original filename
                size:
                  type: integer
                  format: int64
                  description: File size in bytes
                mime_type:
                  type: string
                  description: MIME type of the file
                checksum:
                  type: string
                  description: SHA-256 checksum of the file (optional)
                context:
                  type: string
                  description: File context (e.g., admin_import, export, error_report)
                context_id:
                  type: string
                  format: uuid
                  description: ID of the entity this file is associated with
                access_level:
                  type: string
                  enum: [private, authenticated, public]
                  default: private
                  description: Access level for the file
      responses:
        '200':
          description: Presigned upload URL generated
          content:
            application/json:
              schema:
                type: object
                required:
                  - file_id
                  - upload_url
                  - upload_method
                  - expires_at
                properties:
                  file_id:
                    type: string
                    format: uuid
                  upload_url:
                    type: string
                    description: Presigned S3 URL for upload
                  upload_method:
                    type: string
                    description: HTTP method to use (typically PUT)
                  upload_headers:
                    type: object
                    additionalProperties:
                      type: string
                    description: Headers to include in upload request
                  expires_at:
                    type: string
                    format: date-time
                    description: When the presigned URL expires
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/files/{fileId}/complete:
    parameters:
      - name: fileId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      summary: Complete file upload
      description: Confirm that file upload to S3 has completed successfully
      operationId: completeFileUpload
      tags:
        - Files
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                checksum:
                  type: string
                  description: SHA-256 checksum for verification
      responses:
        '200':
          description: File upload completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/File'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/files/{fileId}:
    parameters:
      - name: fileId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get file metadata
      operationId: getFile
      tags:
        - Files
      responses:
        '200':
          description: File metadata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/File'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      summary: Delete file
      operationId: deleteFile
      tags:
        - Files
      responses:
        '204':
          description: File deleted successfully
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/files/{fileId}/download:
    parameters:
      - name: fileId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get file download URL
      description: Get a presigned URL for downloading a file
      operationId: getFileDownloadURL
      tags:
        - Files
      responses:
        '200':
          description: Download URL generated
          content:
            application/json:
              schema:
                type: object
                required:
                  - url
                  - expires_at
                properties:
                  url:
                    type: string
                    description: Presigned download URL
                  expires_at:
                    type: string
                    format: date-time
                    description: When the URL expires
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/data_tables:
    get:
      summary: List all data tables in a project
      operationId: listDataTables
      tags:
        - Data Tables
      parameters:
        - name: project_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: Project ID
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of data tables
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/DataTable'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
    post:
      summary: Create a new data table
      operationId: createDataTable
      tags:
        - Data Tables
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DataTableCreate'
      responses:
        '201':
          description: Data table created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataTable'
        '400':
          $ref: '#/components/responses/BadRequest'

  /v1/data_tables/{tableId}:
    parameters:
      - $ref: '#/components/parameters/TableId'
    get:
      summary: Get data table by ID
      operationId: getDataTable
      tags:
        - Data Tables
      responses:
        '200':
          description: Data table details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataTable'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Update data table
      operationId: updateDataTable
      tags:
        - Data Tables
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DataTableUpdate'
      responses:
        '200':
          description: Data table updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataTable'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      summary: Delete data table
      operationId: deleteDataTable
      tags:
        - Data Tables
      responses:
        '204':
          description: Data table deleted
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/data_tables/{tableId}/rows:
    parameters:
      - $ref: '#/components/parameters/TableId'
    get:
      summary: List data table rows
      operationId: listDataTableRows
      tags:
        - Data Tables
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 100
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
        - name: sort
          in: query
          schema:
            type: string
            enum: [asc, desc]
            default: asc
          description: Sort order by creation date (asc = oldest first, desc = newest first)
      responses:
        '200':
          description: List of rows
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/DataTableRow'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      summary: Create a new row in data table
      operationId: createDataTableRow
      tags:
        - Data Tables
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                answers:
                  type: object
                  description: Initial cell answer values for the row
                  additionalProperties: true
      responses:
        '201':
          description: Row created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataTableRow'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/data_tables/{tableId}/rows/{rowId}:
    parameters:
      - $ref: '#/components/parameters/TableId'
      - $ref: '#/components/parameters/RowId'
    get:
      summary: Get data table row
      operationId: getDataTableRow
      tags:
        - Data Tables
      responses:
        '200':
          description: Row details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataTableRow'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Update data table row
      operationId: updateDataTableRow
      tags:
        - Data Tables
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DataTableRowUpdate'
      responses:
        '200':
          description: Row updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataTableRow'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      summary: Delete data table row
      operationId: deleteDataTableRow
      tags:
        - Data Tables
      responses:
        '204':
          description: Row deleted
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/workflows:
    get:
      summary: List workflow definitions for a form version
      operationId: listWorkflows
      tags:
        - Workflows
      parameters:
        - name: form_version_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: Form version ID
      responses:
        '200':
          description: List of workflow definitions
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WorkflowDefinition'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      summary: Create a new workflow definition
      operationId: createWorkflow
      tags:
        - Workflows
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                form_version_id:
                  type: string
                  format: uuid
                  description: Form version ID
                title:
                  type: string
                  minLength: 1
                description:
                  type: string
                triggers:
                  type: array
                  items:
                    $ref: '#/components/schemas/Trigger'
                  default: []
                steps:
                  type: array
                  items:
                    $ref: '#/components/schemas/WorkflowStep'
                  default: []
              required:
                - form_version_id
                - title
      responses:
        '201':
          description: Workflow created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowDefinition'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/workflows/{workflowId}:
    parameters:
      - $ref: '#/components/parameters/WorkflowId'
    get:
      summary: Get a workflow definition
      operationId: getWorkflow
      tags:
        - Workflows
      parameters:
        - name: form_version_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: Form version ID that contains this workflow
      responses:
        '200':
          description: Workflow definition
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowDefinition'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      summary: Update a workflow definition
      operationId: updateWorkflow
      tags:
        - Workflows
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  minLength: 1
                description:
                  type: string
              required:
                - title
      responses:
        '200':
          description: Workflow updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowDefinition'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      summary: Delete a workflow definition
      operationId: deleteWorkflow
      tags:
        - Workflows
      responses:
        '204':
          description: Workflow deleted
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Partially update a workflow definition
      operationId: patchWorkflow
      tags:
        - Workflows
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  minLength: 1
                description:
                  type: string
      responses:
        '200':
          description: Workflow updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowDefinition'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/workflows/{workflowId}/steps/insert:
    parameters:
      - $ref: '#/components/parameters/WorkflowId'
    post:
      summary: Insert a new step into the workflow
      operationId: insertStep
      tags:
        - Workflows
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                type:
                  type: string
                  enum: [EMAIL, CONDITION, CALCULATION, UPDATE_VALUE, CREATE_ENTRY, DELETE_ENTRY, API_REQUEST, FOR_EACH]
                position:
                  type: string
                  enum: [first, before, after]
                target_step_id:
                  type: string
                config:
                  type: object
              required:
                - type
                - position
      responses:
        '201':
          description: Step created
          content:
            application/json:
              schema:
                type: object
                properties:
                  step:
                    $ref: '#/components/schemas/WorkflowStep'
                  workflow:
                    $ref: '#/components/schemas/WorkflowDefinition'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/workflows/{workflowId}/steps/{stepId}:
    parameters:
      - $ref: '#/components/parameters/WorkflowId'
      - name: stepId
        in: path
        required: true
        schema:
          type: string
    delete:
      summary: Delete a workflow step
      operationId: deleteStep
      tags:
        - Workflows
      responses:
        '200':
          description: Step deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  workflow:
                    $ref: '#/components/schemas/WorkflowDefinition'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Update step configuration
      operationId: patchStepConfig
      tags:
        - Workflows
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                config:
                  type: object
              required:
                - config
      responses:
        '200':
          description: Step config updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  step:
                    $ref: '#/components/schemas/WorkflowStep'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/workflows/{workflowId}/connections:
    parameters:
      - $ref: '#/components/parameters/WorkflowId'
    post:
      summary: Add a connection between steps
      operationId: addConnection
      tags:
        - Workflows
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                source_step_id:
                  type: string
                path:
                  type: string
                target_step_id:
                  type: string
              required:
                - source_step_id
                - path
                - target_step_id
      responses:
        '200':
          description: Connection added
          content:
            application/json:
              schema:
                type: object
                properties:
                  workflow:
                    $ref: '#/components/schemas/WorkflowDefinition'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      summary: Delete a connection between steps
      operationId: deleteConnection
      tags:
        - Workflows
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                source_step_id:
                  type: string
                path:
                  type: string
                target_step_id:
                  type: string
              required:
                - source_step_id
                - path
                - target_step_id
      responses:
        '200':
          description: Connection deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  workflow:
                    $ref: '#/components/schemas/WorkflowDefinition'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Reconnect a connection to a new target
      operationId: reconnectConnection
      tags:
        - Workflows
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                current:
                  type: object
                  properties:
                    source_step_id:
                      type: string
                    path:
                      type: string
                    target_step_id:
                      type: string
                  required:
                    - source_step_id
                    - path
                    - target_step_id
                new:
                  type: object
                  properties:
                    source_step_id:
                      type: string
                    path:
                      type: string
                    target_step_id:
                      type: string
                  required:
                    - source_step_id
                    - path
                    - target_step_id
              required:
                - current
                - new
      responses:
        '200':
          description: Connection reconnected
          content:
            application/json:
              schema:
                type: object
                properties:
                  workflow:
                    $ref: '#/components/schemas/WorkflowDefinition'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/workflows/{workflowId}/triggers:
    parameters:
      - $ref: '#/components/parameters/WorkflowId'
    post:
      summary: Add a trigger to the workflow
      operationId: createTrigger
      tags:
        - Workflows
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                type:
                  type: string
                  enum:
                    [
                      form_submitted,
                      answer_updated,
                      metafield_updated,
                      form_entry_created,
                      form_page_loaded,
                      api_request_received,
                      button_clicked,
                    ]
                field_ids:
                  type: array
                  items:
                    type: string
                page_ids:
                  type: array
                  items:
                    type: string
              required:
                - type
      responses:
        '201':
          description: Trigger created
          content:
            application/json:
              schema:
                type: object
                properties:
                  trigger:
                    $ref: '#/components/schemas/Trigger'
                  workflow:
                    $ref: '#/components/schemas/WorkflowDefinition'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/workflows/{workflowId}/triggers/{triggerId}:
    parameters:
      - $ref: '#/components/parameters/WorkflowId'
      - name: triggerId
        in: path
        required: true
        schema:
          type: string
        description: Trigger ID
    patch:
      summary: Update a trigger
      operationId: patchTrigger
      tags:
        - Workflows
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                start_step_id:
                  type: string
                field_ids:
                  type: array
                  items:
                    type: string
                page_ids:
                  type: array
                  items:
                    type: string
      responses:
        '200':
          description: Trigger updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  trigger:
                    $ref: '#/components/schemas/Trigger'
                  workflow:
                    $ref: '#/components/schemas/WorkflowDefinition'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      summary: Delete a trigger
      operationId: deleteTrigger
      tags:
        - Workflows
      responses:
        '200':
          description: Trigger deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  workflow:
                    $ref: '#/components/schemas/WorkflowDefinition'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/workflows/{workflowId}/runs:
    parameters:
      - $ref: '#/components/parameters/WorkflowId'
    get:
      summary: List workflow runs for a specific workflow
      operationId: listWorkflowRunsScoped
      tags:
        - Workflows
      parameters:
        - name: entry_id
          in: query
          schema:
            type: string
            format: uuid
          description: Filter by entry ID
        - name: status
          in: query
          schema:
            type: string
            enum: [started, completed, failed]
          description: Filter by status
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
          description: Maximum number of results
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
          description: Number of results to skip
      responses:
        '200':
          description: List of workflow runs
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WorkflowRun'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      summary: Execute a workflow manually for a specific entry
      operationId: executeWorkflow
      tags:
        - Workflows
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                entry_id:
                  type: string
                  format: uuid
                  description: Entry ID to execute workflow against
              required:
                - entry_id
      responses:
        '200':
          description: Workflow executed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRunDetail'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/workflows/{workflowId}/runs/{runId}:
    parameters:
      - $ref: '#/components/parameters/WorkflowId'
      - name: runId
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: Workflow run ID
    get:
      summary: Get workflow run details
      operationId: getWorkflowRunScoped
      tags:
        - Workflows
      responses:
        '200':
          description: Workflow run details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRunDetail'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/references/search:
    get:
      summary: Search for reference options
      description: |
        Returns available reference options based on context and current selection.
        Used for building references in workflows, calculations, and conditional logic.
      operationId: searchReferences
      tags:
        - References
      parameters:
        - name: form_version_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: Form version ID
        - name: q
          in: query
          schema:
            type: string
            default: ''
          description: Search query to filter results
        - name: context_type
          in: query
          required: true
          schema:
            type: string
            enum:
              [
                field_display_logic,
                field_display_logic_value,
                field_validation_value,
                field_default_value,
                workflow_step,
                options_data_source,
                create_entry,
                update_field,
                delete_entry,
                view_filter,
                view_column,
              ]
          description: Context in which the reference is being built
        - name: cardinality
          in: query
          schema:
            type: string
            enum: [single, list]
            default: single
          description: Whether reference should resolve to a single value or list
        - name: current_selection
          in: query
          schema:
            type: string
            default: ''
          description: Current reference path for navigation
        - name: workflow_id
          in: query
          schema:
            type: string
          description: Workflow ID if in workflow context
        - name: node_id
          in: query
          schema:
            type: string
          description: Workflow node/step ID if in workflow step context
        - name: calculation_id
          in: query
          schema:
            type: string
          description: Calculation ID if in calculation context
        - name: base_reference
          in: query
          schema:
            type: string
          description: Base reference that constrains navigation (user cannot go higher than this)
        - name: allow_formatters
          in: query
          schema:
            type: boolean
            default: false
          description: Whether to show formatter options for list references
        - name: template_loop_parent_ref
          in: query
          schema:
            type: string
          description: |
            Reference to the loop collection when searching inside a template for loop.
            When set, enables template loop item context with access to iter_item and forloop variables.
            Example: "entry:current.answers.orders"
        - name: expected_types
          in: query
          schema:
            type: string
          description: |
            Comma-separated list of expected value types for filtering results by type compatibility.
            Incompatible items will have is_selectable=false and type_compatible=false.
            Items are still shown but sorted with compatible items first.
            Unknown types are always considered compatible (for dynamic data like workflow outputs).
            Valid types: unknown, string, number, boolean, date, datetime, time, array.
            Example: "number,string" for a numeric comparison that accepts coercible strings.
      responses:
        '200':
          description: Available reference options with back navigation
          content:
            application/json:
              schema:
                type: object
                properties:
                  back_reference:
                    type: string
                    nullable: true
                    description: Reference path to navigate back to from current selection
                  options:
                    type: array
                    items:
                      $ref: '#/components/schemas/ReferenceOption'
                required:
                  - options
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/references/labels:
    post:
      summary: Get labels for reference strings
      description: |
        Batch fetch human-readable labels and icons for reference strings.
        Returns contextual and absolute labels for use in template editors.
      operationId: getReferenceLabels
      tags:
        - References
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                references:
                  type: array
                  items:
                    type: string
                  maxItems: 50
                  description: Array of reference strings to get labels for
              required:
                - references
      responses:
        '200':
          description: Labels for requested references
          content:
            application/json:
              schema:
                type: object
                properties:
                  labels:
                    type: array
                    items:
                      type: object
                      properties:
                        reference:
                          type: string
                          description: The reference string
                        label:
                          type: string
                          description: Contextual label (last segment)
                        absolute_label:
                          type: string
                          description: Full descriptive label with path
                        icon:
                          type: string
                          description: Contextual icon emoji
                        absolute_icon:
                          type: string
                          description: Absolute icon emoji for reference type
                        value_type:
                          type: string
                          enum: [unknown, string, number, boolean, date, datetime, time, array]
                          description: Semantic type of the value this reference resolves to (for operator filtering)
                        error:
                          type: string
                          description: Error message if label generation failed
                required:
                  - labels
        '400':
          $ref: '#/components/responses/BadRequest'

  /v1/templates/validate:
    post:
      summary: Validate pongo2 template syntax
      description: |
        Validates a pongo2 template string for syntax errors.
        Returns validation status and any error messages.
      operationId: validateTemplate
      tags:
        - Templates
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                template:
                  type: string
                  description: The template string to validate
                context_type:
                  type: string
                  description: Optional context type for additional validation
                workflow_id:
                  type: string
                  description: Optional workflow ID if validating in workflow context
                node_id:
                  type: string
                  description: Optional node ID if validating in workflow step context
              required:
                - template
      responses:
        '200':
          description: Validation result
          content:
            application/json:
              schema:
                type: object
                properties:
                  valid:
                    type: boolean
                    description: Whether the template is valid
                  errors:
                    type: array
                    items:
                      type: string
                    description: List of validation errors
                  warnings:
                    type: array
                    items:
                      type: string
                    description: List of validation warnings
                required:
                  - valid
        '400':
          $ref: '#/components/responses/BadRequest'

  /v1/entries/{entryId}/workflow_runs:
    parameters:
      - name: entryId
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: Entry ID
    get:
      summary: List all workflow runs for a specific entry
      operationId: listEntryWorkflowRuns
      tags:
        - Entries
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [started, completed, failed]
          description: Filter by status
        - name: workflow_id
          in: query
          schema:
            type: string
          description: Filter by specific workflow ID
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
          description: Maximum number of results
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
          description: Number of results to skip
      responses:
        '200':
          description: List of workflow runs for the entry
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WorkflowRun'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/views:
    get:
      summary: List views for a form
      operationId: listViews
      tags:
        - Views
      parameters:
        - name: form_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: Form ID
      responses:
        '200':
          description: List of views
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/View'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      summary: Create a new view
      operationId: createView
      tags:
        - Views
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ViewCreate'
      responses:
        '201':
          description: View created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/View'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/views/{viewId}:
    parameters:
      - name: viewId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get a view by ID
      operationId: getView
      tags:
        - Views
      responses:
        '200':
          description: View details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/View'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Update a view
      operationId: updateView
      tags:
        - Views
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ViewUpdate'
      responses:
        '200':
          description: View updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/View'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      summary: Delete a view
      operationId: deleteView
      tags:
        - Views
      responses:
        '204':
          description: View deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/views/{viewId}/apply:
    parameters:
      - name: viewId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Apply a view and get filtered entries with column values
      operationId: applyViewToEntries
      tags:
        - Views
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Entries with applied view
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EntriesWithView'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/exports:
    post:
      summary: Create a CSV export job
      operationId: createExport
      tags:
        - Forms
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - view_id
              properties:
                view_id:
                  type: string
                  format: uuid
                format:
                  type: string
                  default: csv
      responses:
        '201':
          description: Export job created
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id:
                    type: string
                    format: uuid
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    get:
      summary: List export files for a form
      operationId: listExports
      tags:
        - Forms
      parameters:
        - name: form_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: Form ID
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of export files
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/File'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/imports:
    post:
      summary: Create a CSV import job
      operationId: createImport
      tags:
        - Forms
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - file_id
                - form_id
              properties:
                file_id:
                  type: string
                  format: uuid
                form_id:
                  type: string
                  format: uuid
                  description: Form ID
      responses:
        '201':
          description: Import job created
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id:
                    type: string
                    format: uuid
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /v1/jobs:
    get:
      summary: List jobs
      description: List jobs with optional filtering by type, status, owner_type, owner_id, or form_id
      operationId: listJobs
      tags:
        - Jobs
      parameters:
        - name: type
          in: query
          required: false
          schema:
            type: string
          description: Filter by job type (e.g., export_csv, import_csv)
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum: [pending, processing, completed, failed]
          description: Filter by job status
        - name: owner_type
          in: query
          required: false
          schema:
            type: string
          description: Filter by owner type (e.g., form, user, system)
        - name: owner_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Filter by owner ID
        - name: form_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Filter by form ID (from job payload)
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 20
            minimum: 1
          description: Maximum number of jobs to return
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            default: 0
            minimum: 0
          description: Number of jobs to skip
      responses:
        '200':
          description: List of jobs
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Job'
                  meta:
                    type: object
                    properties:
                      total:
                        type: integer
                      limit:
                        type: integer
                      offset:
                        type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/jobs/{jobId}:
    parameters:
      - name: jobId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get job status
      operationId: getJob
      tags:
        - Jobs
      responses:
        '200':
          description: Job details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '404':
          $ref: '#/components/responses/NotFound'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API_KEY
      description: |
        API key authentication. 

        Format: `fwk_live_<64_hex_characters>`

        Example: `Authorization: Bearer fwk_live_abc123...`

    entryKeyAuth:
      type: apiKey
      in: header
      name: X-Formwork-Entry-Key
      description: |
        Entry-specific authentication key for client API.

        This key is returned once when creating an entry and must be stored by the client.

        Example: `X-Formwork-Entry-Key: <entry_key_value>`

  parameters:
    AccountId:
      name: accountId
      in: path
      required: true
      schema:
        type: string
        format: uuid

    ProjectId:
      name: projectId
      in: path
      required: true
      schema:
        type: string
        format: uuid

    UserId:
      name: userId
      in: path
      required: true
      schema:
        type: string
        format: uuid

    FormId:
      name: formId
      in: path
      required: true
      schema:
        type: string
        format: uuid

    VersionId:
      name: versionId
      in: path
      required: true
      schema:
        type: string
        format: uuid

    WorkflowId:
      name: workflowId
      in: path
      required: true
      schema:
        type: string
      description: Workflow definition ID

    EntryId:
      name: entryId
      in: path
      required: true
      schema:
        type: string
        format: uuid

    TableId:
      name: tableId
      in: path
      required: true
      schema:
        type: string
        format: uuid

    RowId:
      name: rowId
      in: path
      required: true
      schema:
        type: string
        format: uuid

  schemas:
    FormSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum: [normal, data_table, metafields]
        title:
          type: string
        title_internal:
          type: string
        description:
          type: string
        metafields_form_id:
          type: string
          format: uuid
          description: ID of the form used for storing metafields (operational data) for this form's entries
        parent_form_id:
          type: string
          format: uuid
          description: ID of the parent form if this is a metafields form
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        current_version_id:
          type: string
          format: uuid
      required:
        - id
        - type
        - title
        - created_at
        - updated_at

    FormCreate:
      type: object
      properties:
        project_id:
          type: string
          format: uuid
          description: Project ID
        title:
          type: string
          description: Public-facing title shown to form respondents
        title_internal:
          type: string
          description: Internal name for the form (required)
        description:
          type: string
        on_submit_message:
          type: string
        submit_button_text:
          type: string
        pages:
          type: array
          items:
            $ref: '#/components/schemas/Page'
        workflows:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowDefinition'
      required:
        - project_id
        - title
        - title_internal
        - pages

    FormUpdate:
      type: object
      properties:
        title_internal:
          type: string
          description: Internal name for the form
        current_version_id:
          type: string
          description: ID of the form version to set as current
      required: []

    Form:
      allOf:
        - $ref: '#/components/schemas/FormSummary'
        - type: object
          properties:
            current_version:
              $ref: '#/components/schemas/FormVersion'

    FormVersionSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
        form_id:
          type: string
          format: uuid
        version_title:
          type: string
        status:
          type: string
          enum: [draft, published, archived]
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - form_id
        - version_title
        - status
        - created_at
        - updated_at

    FormVersionCreate:
      type: object
      properties:
        form_id:
          type: string
          format: uuid
          description: Form ID
        pages:
          type: array
          items:
            $ref: '#/components/schemas/Page'
        workflows:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowDefinition'
      required:
        - form_id
        - pages

    FormVersionUpdate:
      type: object
      properties:
        title:
          type: string
          description: Public-facing title of the form
        description:
          type: string
          description: Description or instructions shown to users
        submit_button_text:
          type: string
          description: Text displayed on the submit button
        on_submit_message:
          type: string
          description: Message shown after successful submission
        pages:
          type: array
          items:
            $ref: '#/components/schemas/Page'
        workflows:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowDefinition'
      required:
        - pages

    FormVersionPatch:
      type: object
      properties:
        title:
          type: string
          description: Public-facing title of the form
        description:
          type: string
          description: Description or instructions shown to users
        submit_button_text:
          type: string
          description: Text displayed on the submit button
        on_submit_message:
          type: string
          description: Message shown after successful submission
        pages:
          type: array
          items:
            $ref: '#/components/schemas/Page'
          description: Form pages structure
        workflows:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowDefinition'
          description: Workflow definitions
        client_config:
          type: object
          additionalProperties: true
          description: Client-side configuration including theme settings
      description: Partial update schema for form versions - all fields are optional

    FormVersion:
      allOf:
        - $ref: '#/components/schemas/FormVersionSummary'
        - type: object
          properties:
            pages:
              type: array
              items:
                $ref: '#/components/schemas/Page'
            workflows:
              type: array
              items:
                $ref: '#/components/schemas/WorkflowDefinition'
          required:
            - pages

    Page:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
        fields:
          type: array
          items:
            $ref: '#/components/schemas/Field'
      required:
        - id
        - title
        - fields

    Field:
      type: object
      properties:
        id:
          type: string
        label:
          type: string
        type:
          type: string
          enum:
            - text
            - email
            - url
            - textarea
            - hidden
            - number
            - date
            - time
            - datetime
            - toggle
            - select
            - radio
            - slider
            - phone
            - password
            - rating
            - signature
            - file
            - name
            - address
            - group
            - heading
            - paragraph
            - button
            - html
            - image
            - video
        sub_type:
          type: string
          deprecated: true
          description: Deprecated - use type and config instead
        description:
          type: string
        placeholder:
          type: string
        default_value:
          type: string
        config:
          type: object
          description: |
            Type-specific configuration options.

            **Text/Textarea:**
            ```json
            {
              "min_length": 10,
              "max_length": 500,
              "autocomplete": "name",
              "input_mode": "text",
              "spellcheck": true,
              "autocapitalize": "sentences",
              "rows": 5,
              "resize": "vertical"
            }
            ```

            **Number:**
            ```json
            {
              "min": 0,
              "max": 100,
              "step": 0.01,
              "display_prefix": "$",
              "display_suffix": "",
              "decimal_places": 2,
              "thousands_separator": true,
              "input_mode": "decimal"
            }
            ```

            **Date:**
            ```json
            {
              "date_format": "YYYY-MM-DD",
              "min_date": "2024-01-01",
              "max_date": "2025-12-31",
              "disabled_dates": ["2024-12-25"],
              "disabled_days_of_week": [0, 6],
              "default_to_now": false
            }
            ```

            **Toggle:**
            ```json
            {
              "display_style": "toggle",
              "default_value": false,
              "true_label": "Yes",
              "false_label": "No",
              "require_true": false
            }
            ```

            **Select:**
            ```json
            {
              "allow_search": true,
              "placeholder": "Choose an option",
              "clearable": true,
              "allow_other": false,
              "other_label": "Other (please specify)"
            }
            ```

            **Name:**
            ```json
            {
              "show_title": false,
              "show_middle_name": false,
              "show_suffix": false,
              "name_order": "western"
            }
            ```

            **Address:**
            ```json
            {
              "show_street2": true,
              "show_city": true,
              "show_state": true,
              "show_postcode": true,
              "show_country": true,
              "country_mode": "all",
              "allowed_countries": ["US", "GB", "CA"],
              "default_country": "US",
              "state_label": "State",
              "postcode_label": "ZIP Code",
              "enable_lookup": true,
              "lookup_provider": "here",
              "lookup_countries": ["GB", "US"],
              "lookup_min_chars": 3
            }
            ```

            **Signature:**
            ```json
            {
              "size": "standard"
            }
            ```
            Size options: "compact" (400×120), "standard" (500×160), "large" (600×200).
            Signature is stored as PNG data URL.

            **File:**
            ```json
            {
              "max_file_size": 10485760,
              "allowed_types": ["image/*", "application/pdf", ".doc", ".docx"],
              "multiple": false,
              "min_files": 0,
              "max_files": 1
            }
            ```
            File uploads are stored as array of file IDs. Size is in bytes (default 10MB).

            **Heading:**
            ```json
            {
              "level": "h2"
            }
            ```
            The heading text is stored in the `label` field.

            **Paragraph:**
            The paragraph content is stored in the `label` field.

            **Button:**
            ```json
            {
              "type": "link",
              "url": "https://example.com",
              "open_in_new_tab": true
            }
            ```
            The button text is stored in the `label` field.
          additionalProperties: true
        readonly:
          type: boolean
          default: false
        required:
          type: boolean
          default: false
          description: |
            Whether this field is required. 
            Note: Container fields (group) and templated fields (name, address) do NOT support 
            the required property at the parent level. For these field types, configure the 
            required property on individual subfields instead.
        repeatable:
          type: boolean
          default: false
        layout:
          type: string
          enum: [column, row]
        options:
          type: array
          items:
            $ref: '#/components/schemas/FieldOption'
        options_data_source:
          $ref: '#/components/schemas/OptionsDataSource'
        fields:
          type: array
          items:
            $ref: '#/components/schemas/Field'
        validation:
          type: array
          items:
            $ref: '#/components/schemas/ValidationRule'
        conditional_logic:
          $ref: '#/components/schemas/ConditionalLogic'
        conditional_logic_action:
          type: string
          enum: [show, hide]
        is_system_managed:
          type: boolean
          description: Internal flag for system-managed field children
        system_managed_parent_id:
          type: string
          description: ID of parent field for system-managed subfields
      required:
        - id
        - type

    FieldOption:
      type: object
      properties:
        id:
          type: string
        label:
          type: string
        value:
          type: string
      required:
        - id
        - label
        - value

    OptionsDataSource:
      type: object
      description: Dynamic options from data table or form reference
      properties:
        source_reference:
          type: string
          description: Reference to data source
          example: 'data_table:products.name'
        reference_is_simple_value:
          type: boolean
          description: Whether reference returns simple value or object
        label_reference:
          type: string
          description: Reference for option labels (if different from value)
      required:
        - source_reference
        - reference_is_simple_value

    ValidationRule:
      type: object
      properties:
        rule:
          $ref: '#/components/schemas/ConditionalRule'
        message:
          type: string
          nullable: true
      required:
        - rule

    ConditionalLogic:
      type: object
      properties:
        type:
          type: string
          enum: [all, any]
        groups:
          type: array
          items:
            $ref: '#/components/schemas/ConditionalGroup'
      required:
        - type
        - groups

    ConditionalGroup:
      type: object
      properties:
        type:
          type: string
          enum: [all, any]
        rules:
          type: array
          items:
            $ref: '#/components/schemas/ConditionalRule'
      required:
        - type
        - rules

    ConditionalRule:
      type: object
      properties:
        field:
          type: string
          description: Field ID or reference string (e.g. "entry:current.answers.my_field_id")
        operator:
          type: string
          enum:
            - eq
            - neq
            - gt
            - gte
            - lt
            - lte
            - length_eq
            - length_neq
            - length_gt
            - length_gte
            - length_lt
            - length_lte
            - contains
            - not_contains
            - starts_with
            - ends_with
            - matches_pattern
            - is_empty
            - is_not_empty
            - is_email
            - is_url
            - is_number
            - is_integer
            - is_float
            - is_alpha
            - is_alphanum
            - is_true
            - is_false
        value:
          nullable: true
          description: Scalar value or reference string
          oneOf:
            - type: string
            - type: number
            - type: boolean
      required:
        - field
        - operator

    WorkflowDefinition:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
        description:
          type: string
        triggers:
          type: array
          items:
            $ref: '#/components/schemas/Trigger'
        steps:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowStep'
      required:
        - id
        - title
        - triggers
        - steps

    Trigger:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier within workflow (auto-generated from type, e.g., "form_submitted", "answer_updated_1")
        type:
          type: string
          enum:
            - form_submit
            - answer_updated
            - metafield_updated
            - form_entry_created
            - form_page_loaded
            - api_request_received
            - button_clicked
        field_ids:
          type: array
          items:
            type: string
          description: Required for answer_updated and metafield_updated triggers
        page_ids:
          type: array
          items:
            type: string
          description: Optional for form_page_loaded trigger
      required:
        - type

    WorkflowStep:
      type: object
      properties:
        id:
          type: string
        type:
          type: string
          enum:
            - START
            - CALCULATION
            - UPDATE_VALUE
            - CONDITION
            - FOR_EACH
            - API_REQUEST
            - CREATE_ENTRY
            - UPDATE_ENTRY
            - DELETE_ENTRY
        config:
          type: object
          description: Step-specific configuration
        connections:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          description: Map of path names to lists of downstream step IDs
      required:
        - id
        - type

    WorkflowRun:
      type: object
      properties:
        account_id:
          type: string
          format: uuid
          description: Account that owns this workflow run
        workflow_id:
          type: string
          description: ID of the workflow definition
        run_id:
          type: string
          format: uuid
          description: Unique identifier for this execution
        trigger_info:
          $ref: '#/components/schemas/Trigger'
          description: Trigger that initiated this workflow
        owner_type:
          type: string
          description: Type of entity that owns this run (e.g., "entry")
        owner_id:
          type: string
          format: uuid
          description: ID of the owning entity
        started_at:
          type: string
          format: date-time
          description: When the workflow execution started
        ended_at:
          type: string
          format: date-time
          nullable: true
          description: When the workflow execution completed
        status:
          type: string
          enum: [started, completed, failed]
          description: Current status of the workflow execution
        executions:
          type: array
          description: Sequential log of all step executions
          items:
            $ref: '#/components/schemas/StepExecution'
      required:
        - account_id
        - workflow_id
        - run_id
        - trigger_info
        - owner_type
        - owner_id
        - started_at
        - status
        - executions

    WorkflowRunDetail:
      $ref: '#/components/schemas/WorkflowRun'

    StepExecution:
      type: object
      properties:
        run_id:
          type: string
          format: uuid
          description: ID of the workflow run this execution belongs to
        step_id:
          type: string
          description: ID of the step that was executed
        parent_step_id:
          type: string
          nullable: true
          description: ID of the parent step (for steps executed within FOR_EACH loops)
        index_path:
          type: string
          nullable: true
          description: Path indicating nested iteration position (e.g., "0.1.2" for nested loops)
        entered_at:
          type: string
          format: date-time
          description: When the step execution started
        exited_at:
          type: string
          format: date-time
          description: When the step execution completed
        duration_ms:
          type: integer
          format: int64
          description: Duration of execution in milliseconds
        path_fired:
          type: string
          description: Which connection path was taken (e.g., "next", "true", "false", "error")
        output:
          type: object
          description: JSON output data from the step execution
          additionalProperties: true
        error:
          type: string
          nullable: true
          description: Error message if the step failed
      required:
        - run_id
        - step_id
        - entered_at
        - exited_at
        - duration_ms
        - path_fired
        - output

    WorkflowStepExecution:
      $ref: '#/components/schemas/StepExecution'

    EntrySummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
        form_id:
          type: string
          format: uuid
        form_version_id:
          type: string
          format: uuid
        status:
          type: string
          enum: [draft, complete, processing, deleted]
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        submitted_at:
          type: string
          format: date-time
          nullable: true
      required:
        - id
        - form_id
        - form_version_id
        - status
        - created_at
        - updated_at

    Entry:
      allOf:
        - $ref: '#/components/schemas/EntrySummary'
        - type: object
          properties:
            answers:
              type: object
              description: Form answers in flat path notation
              additionalProperties: true
              example:
                'income': 75000
                'contact_info.email': 'user@example.com'
                'addresses[addr_1].street': '123 Main St'
            validation_errors:
              type: object
              description: Validation errors by field path
              additionalProperties:
                type: array
                items:
                  type: string
            field_visibility:
              type: object
              description: Field visibility state by path
              additionalProperties:
                type: boolean
          required:
            - answers

    AnswerUpdate:
      type: object
      properties:
        answers:
          type: object
          description: Field values to update (set to null to remove)
          additionalProperties: true
          example:
            'income': 75000
            'addresses[addr_1].street': '123 Updated St'
            'addresses[addr_2]': null
      required:
        - answers

    AnswerUpdateResponse:
      type: object
      properties:
        changes:
          type: object
          properties:
            answers:
              type: object
              description: Changed answer values
              additionalProperties: true
            validation:
              type: object
              description: Changed validation results
              additionalProperties:
                type: array
                nullable: true
                items:
                  type: string
            visibility:
              type: object
              description: Changed visibility states
              additionalProperties:
                nullable: true
                type: boolean
        entry_status:
          type: string
          enum: [draft, complete, processing]
      required:
        - changes
        - entry_status

    DataTable:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        columns:
          type: array
          items:
            $ref: '#/components/schemas/Field'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - name
        - columns
        - created_at
        - updated_at

    DataTableCreate:
      type: object
      properties:
        project_id:
          type: string
          format: uuid
          description: Project ID
        name:
          type: string
        columns:
          type: array
          items:
            $ref: '#/components/schemas/Field'
      required:
        - project_id
        - name
        - columns

    DataTableUpdate:
      type: object
      properties:
        title_internal:
          type: string
      required:
        - title_internal

    DataTableRow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        table_id:
          type: string
          format: uuid
        data:
          type: object
          description: Row data in flat path notation
          additionalProperties: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - table_id
        - data
        - created_at
        - updated_at

    DataTableRowUpdate:
      type: object
      properties:
        answers:
          type: object
          description: Cell answer values to update
          additionalProperties: true
      required:
        - answers

    APIKey:
      type: object
      properties:
        id:
          type: string
          format: uuid
        account_id:
          type: string
          format: uuid
        user_id:
          type: string
          format: uuid
        name:
          type: string
          description: User-friendly name for the API key
          example: 'Production Server'
        key_prefix:
          type: string
          description: First 8 characters of the key for display
          example: 'fwk_live'
        last_used_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        revoked_at:
          type: string
          format: date-time
          nullable: true
      required:
        - id
        - account_id
        - user_id
        - name
        - key_prefix
        - created_at

    APIKeyCreate:
      type: object
      properties:
        name:
          type: string
          description: User-friendly name for the API key
          example: 'Production Server'
      required:
        - name

    APIKeyCreateResponse:
      type: object
      properties:
        key:
          type: string
          description: |
            The full API key. 

            **IMPORTANT:** This is shown only once! Store it securely.
          example: 'fwk_live_abc123456789abcdef0123456789abcdef0123456789abcdef0123456789ab'
        api_key:
          $ref: '#/components/schemas/APIKey'
      required:
        - key
        - api_key

    Project:
      type: object
      properties:
        id:
          type: string
          format: uuid
        account_id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - account_id
        - name
        - created_at
        - updated_at

    ProjectCreate:
      type: object
      properties:
        name:
          type: string
          minLength: 1
        description:
          type: string
      required:
        - name

    ProjectUpdate:
      type: object
      properties:
        name:
          type: string
          minLength: 1
        description:
          type: string
      required:
        - name

    Account:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - name
        - created_at
        - updated_at

    AccountCreate:
      type: object
      properties:
        name:
          type: string
          minLength: 1
      required:
        - name

    AccountUpdate:
      type: object
      properties:
        name:
          type: string
          minLength: 1
      required:
        - name

    AccountUser:
      type: object
      properties:
        account_id:
          type: string
          format: uuid
        user_id:
          type: string
          format: uuid
        role:
          type: string
          enum: [admin, standard]
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - account_id
        - user_id
        - role
        - created_at
        - updated_at

    AccountUserDetails:
      type: object
      properties:
        user_id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        role:
          type: string
          enum: [admin, standard]
        joined_at:
          type: string
          format: date-time
      required:
        - user_id
        - email
        - role
        - joined_at

    ClientFormSchema:
      type: object
      description: Sanitized form schema for public consumption without sensitive data
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        description:
          type: string
        pages:
          type: array
          items:
            $ref: '#/components/schemas/ClientPage'
        submit_button_text:
          type: string
        on_submit_message:
          type: string
        default_answers:
          type: object
          description: |
            Initial default answers for all fields, computed server-side using the same
            logic as entry creation. Keys are flat field paths, values are primitive
            JSON values (string, number, boolean, array).
          additionalProperties: true
        default_field_visibility:
          type: object
          description: |
            Initial field visibility state by path, computed as if a new entry had been
            created but without persisting it. Used so clients can hide/show fields
            correctly before the first answer update.
          additionalProperties:
            type: boolean
        default_validation_errors:
          type: object
          description: |
            Initial validation errors by field path for a new entry with default answers.
            Typically empty, but included for completeness.
          additionalProperties:
            type: array
            items:
              type: string
      required:
        - id
        - title
        - pages

    ClientPage:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
        description:
          type: string
        fields:
          type: array
          items:
            $ref: '#/components/schemas/ClientField'
      required:
        - id
        - title
        - fields

    ClientField:
      type: object
      description: Sanitized field definition for public client API
      properties:
        id:
          type: string
        label:
          type: string
        type:
          type: string
          description: Field type (same enum as Field.type)
        sub_type:
          type: string
          deprecated: true
          description: Deprecated - kept for backward compatibility
        description:
          type: string
        placeholder:
          type: string
        config:
          type: object
          description: Type-specific configuration (sanitized)
          additionalProperties: true
        default_value:
          type: string
        readonly:
          type: boolean
          default: false
        repeatable:
          type: boolean
          default: false
        layout:
          type: string
          enum: [column, row]
        options:
          type: array
          items:
            $ref: '#/components/schemas/FieldOption'
        fields:
          type: array
          items:
            $ref: '#/components/schemas/ClientField'
      required:
        - id
        - type

    ClientEntry:
      type: object
      description: Client-friendly entry representation with flat answers map
      properties:
        id:
          type: string
          format: uuid
        account_id:
          type: string
          format: uuid
          description: Account ID for API requests requiring X-Account-ID header
        form_id:
          type: string
          format: uuid
        status:
          type: string
          enum: [draft, complete, processing]
        entry_key:
          type: string
          description: Only returned on entry creation
        answers:
          type: object
          description: Flat key-value map of answers
          additionalProperties: true
          example:
            'personal_info.first_name': 'John'
            'personal_info.last_name': 'Doe'
            'tags': ['instance123', 'instance456']
            'tags[instance123]': 'Technology'
            'tags[instance456]': 'Health'
        validation_errors:
          type: object
          description: Validation errors by field path
          additionalProperties:
            type: array
            items:
              type: string
          example:
            'personal_info.first_name': ['First name is required']
        field_visibility:
          type: object
          description: Field visibility state by path
          additionalProperties:
            type: boolean
          example:
            'conditional_field': false
            'personal_info.first_name': true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        submitted_at:
          type: string
          format: date-time
          nullable: true
        admin_editing_started_at:
          type: string
          format: date-time
          nullable: true
          description: When an admin edit session started for this entry, if any
      required:
        - id
        - account_id
        - form_id
        - status
        - answers
        - validation_errors
        - field_visibility
        - created_at
        - updated_at

    AddressSuggestion:
      type: object
      description: Address autocomplete suggestion with structured components
      properties:
        id:
          type: string
          description: Provider-specific suggestion identifier
          example: 'here:af:street:1234567890abcdef'
        label:
          type: string
          description: Human-readable formatted address
          example: '10 Downing Street, London, SW1A 2AA, United Kingdom'
        components:
          type: object
          description: Structured address components for form field population
          properties:
            street:
              type: string
              description: Street address with house number
              example: '10 Downing Street'
            street2:
              type: string
              description: Additional address line (apartment, suite, etc)
              example: ''
            city:
              type: string
              description: City or locality
              example: 'London'
            state:
              type: string
              description: State, province, or region
              example: 'England'
            postcode:
              type: string
              description: Postal code or ZIP code
              example: 'SW1A 2AA'
            country:
              type: string
              description: ISO 3166-1 alpha-2 country code
              example: 'GB'
          required:
            - street
            - street2
            - city
            - state
            - postcode
            - country
      required:
        - id
        - label
        - components

    ReferenceOption:
      type: object
      properties:
        label:
          type: string
          description: Human-readable label for the option (contextual)
        absolute_label:
          type: string
          description: Full descriptive label with complete path
        reference:
          type: string
          nullable: true
          description: Full reference path string
        icon:
          type: string
          description: Icon identifier for UI (contextual)
        absolute_icon:
          type: string
          description: Icon representing the complete reference type
        value_type:
          type: string
          enum: [unknown, string, number, boolean, date, datetime, time, array]
          description: Semantic type of the value this reference resolves to (for operator filtering)
        is_navigable:
          type: boolean
          description: Whether this option can be navigated into
        is_selectable:
          type: boolean
          description: Whether this option can be selected as final value
        type_compatible:
          type: boolean
          nullable: true
          description: |
            Indicates type compatibility when expected_types was specified.
            null = no type constraint applied, true = compatible, false = incompatible.
            Incompatible items have is_selectable=false and are sorted to the bottom.
        category:
          type: string
          description: Category for grouping options
        description:
          type: string
          description: Detailed description of the option
        metadata:
          type: object
          additionalProperties: true
          description: Additional metadata for the option
      required:
        - label
        - absolute_label
        - icon
        - absolute_icon
        - is_navigable
        - is_selectable

    File:
      type: object
      properties:
        id:
          type: string
          format: uuid
        account_id:
          type: string
          format: uuid
        context:
          type: string
        context_id:
          type: string
          format: uuid
        status:
          type: string
          enum: [pending, uploading, completed, failed, deleted]
        filename:
          type: string
        original_filename:
          type: string
        size_bytes:
          type: integer
          format: int64
        mime_type:
          type: string
        public_url:
          type: string
          nullable: true
          description: Public URL if file is publicly accessible
        uploaded_at:
          type: string
          format: date-time
          nullable: true
        expires_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - account_id
        - context
        - context_id
        - status
        - filename
        - original_filename
        - size_bytes
        - mime_type
        - created_at
        - updated_at

    Job:
      type: object
      properties:
        id:
          type: string
          format: uuid
        account_id:
          type: string
          format: uuid
        type:
          type: string
        status:
          type: string
          enum: [pending, processing, completed, failed]
        payload:
          type: object
          additionalProperties: true
        result:
          type: object
          additionalProperties: true
          nullable: true
        error:
          type: string
          nullable: true
        attempts:
          type: integer
        max_attempts:
          type: integer
        created_at:
          type: string
          format: date-time
        started_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - account_id
        - type
        - status
        - payload
        - attempts
        - max_attempts
        - created_at
        - updated_at

    PaginationMeta:
      type: object
      properties:
        limit:
          type: integer
        offset:
          type: integer
        total:
          type: integer
      required:
        - limit
        - offset
        - total

    Error:
      type: object
      properties:
        error:
          type: string
        message:
          type: string
      required:
        - error

    View:
      type: object
      properties:
        id:
          type: string
          format: uuid
        account_id:
          type: string
          format: uuid
        form_id:
          type: string
          format: uuid
        name:
          type: string
        is_default:
          type: boolean
        filter_reference:
          type: string
          description: Reference string for filtering entries (e.g., "form:formID.entries[status=complete]")
        column_references:
          type: array
          items:
            $ref: '#/components/schemas/ViewColumnReference'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - account_id
        - form_id
        - name
        - is_default
        - column_references
        - created_at
        - updated_at

    ViewCreate:
      type: object
      properties:
        name:
          type: string
        is_default:
          type: boolean
          default: false
        filter_reference:
          type: string
          description: Reference string for filtering entries (e.g., "form:formID.entries[status=complete]")
        column_references:
          type: array
          items:
            $ref: '#/components/schemas/ViewColumnReference'
      required:
        - name

    ViewUpdate:
      type: object
      properties:
        name:
          type: string
        is_default:
          type: boolean
        filter_reference:
          type: string
          description: Reference string for filtering entries
        column_references:
          type: array
          items:
            $ref: '#/components/schemas/ViewColumnReference'

    ViewColumnReference:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the column
        reference:
          type: string
          description: Reference string to the field (e.g., "entry:current.status", "entry:current.answers.customer_name")
        order:
          type: integer
          description: Display order of the column
      required:
        - id
        - reference
        - order

    ViewSort:
      type: object
      properties:
        field:
          type: string
          description: Field to sort by
        direction:
          type: string
          enum: [asc, desc]
      required:
        - field
        - direction

    EntriesWithView:
      type: object
      properties:
        entries:
          type: array
          items:
            $ref: '#/components/schemas/EntryWithColumns'
        meta:
          $ref: '#/components/schemas/PaginationMeta'
        view:
          $ref: '#/components/schemas/View'
      required:
        - entries
        - meta

    EntryWithColumns:
      type: object
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum: [draft, complete, processing, error, deleted]
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        submitted_at:
          type: string
          format: date-time
          nullable: true
        columns:
          type: object
          additionalProperties: true
          description: Dynamic columns based on view configuration
      required:
        - id
        - status
        - created_at
        - updated_at
        - columns

    EntryRevision:
      type: object
      properties:
        id:
          type: string
          format: uuid
        entry_id:
          type: string
          format: uuid
        revision_number:
          type: integer
        revised_by_user_id:
          type: string
          format: uuid
          nullable: true
        revised_by_type:
          type: string
          enum: [user, admin, system, workflow]
        revision_reason:
          type: string
        snapshot:
          type: object
          description: Full entry answers at this revision
          additionalProperties: true
        changes_summary:
          type: object
          description: Fields that changed in this revision
          additionalProperties:
            type: object
            properties:
              old_value:
                type: object
                description: Previous answer value
              new_value:
                type: object
                description: New answer value
        created_at:
          type: string
          format: date-time
      required:
        - id
        - entry_id
        - revision_number
        - revised_by_type
        - snapshot
        - created_at

  responses:
    BadRequest:
      description: Bad request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Unauthorized - missing or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
