> ## Documentation Index
> Fetch the complete documentation index at: https://formhug.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Finalize prepared rich-text image uploads

> Turn prepared `rich_text_image` uploads into stored images and get back their
public `image_url`s, in request order. Call this after PUTting each file to
its `upload_url` from POST /api/v1/attachments (purpose `rich_text_image`).

All-or-nothing: if any entry is invalid the whole request fails (422) with a
per-entry error under `error_details`, and nothing is consumed. Entries that
were already processed are remembered, so retrying with the same `upload_ids`
does not create duplicates. Embed the returned `image_url`s into your
rich-text HTML. No PAT scope required.




## OpenAPI

````yaml /api-reference/openapi.yaml post /api/v1/attachment_commitments
openapi: 3.0.1
info:
  title: FormHug API V1
  version: v1
  description: |
    FormHug public REST API. All request/response keys are snake_case.

    Successful response:

    ```json
    {
      "data": { ... }
    }
    ```

    Paginated response:

    ```json
    {
      "data": [ ... ],
      "pagination": {
        "total": 123,
        "next_cursor": "NQ=="
      }
    }
    ```

    Error response:

    ```json
    {
      "error": "Human-readable message",
      "error_details": [
        { "attribute": "field_name", "message": "specific error" }
      ]
    }
    ```

    `error_details` only appears for model validation failures.
servers:
  - url: https://formhug.ai
security: []
tags:
  - name: Forms
    description: Form CRUD
  - name: Attachments
    description: >-
      2-step upload for form-element images (theme, rich-text, or field-element
      images). Prepare returns an `upload_id`; consume it in a theme update,
      attachment_commitments, or the form `fields` payload depending on purpose.
  - name: Entries
    description: Entries collected by a form owned by the current user
  - name: Folders
    description: Folder CRUD
  - name: Participated Forms
    description: Forms the current user has submitted to (does not own)
  - name: Published Form Entries
    description: Submit entries to a published form
  - name: Published Forms
    description: Read a published form's public structure for filling
  - name: Linked Form Options
    description: >-
      Discover candidate entries (and their `entry_token`s) for a `linked_form`
      field while filling a published form
  - name: Entry Attachments
    description: >-
      2-step upload for `attachment` field submissions. Prepare returns an
      `upload_id`; submit it under the attachment field in `field_values` when
      creating the entry.
  - name: Me
    description: Current authenticated user
  - name: Webhooks
    description: Webhook integrations attached to a form
  - name: Form Themes
    description: Read and update a form's visual theme
  - name: Form Settings
    description: >-
      Per-form settings — submission flow, availability, access control,
      presentation
  - name: Field Rules
    description: >-
      Read and replace a form's conditional field-display and post-submission
      redirect rules.
  - name: Form Folder
    description: Per-user folder placement for a shared form
  - name: OAuth
    description: OAuth 2.0 PKCE token issuance and revocation
paths:
  /api/v1/attachment_commitments:
    post:
      tags:
        - Attachments
      summary: Finalize prepared rich-text image uploads
      description: >
        Turn prepared `rich_text_image` uploads into stored images and get back
        their

        public `image_url`s, in request order. Call this after PUTting each file
        to

        its `upload_url` from POST /api/v1/attachments (purpose
        `rich_text_image`).


        All-or-nothing: if any entry is invalid the whole request fails (422)
        with a

        per-entry error under `error_details`, and nothing is consumed. Entries
        that

        were already processed are remembered, so retrying with the same
        `upload_ids`

        does not create duplicates. Embed the returned `image_url`s into your

        rich-text HTML. No PAT scope required.
      parameters: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AttachmentCommitmentRequest'
      responses:
        '200':
          description: Images stored
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/AttachmentCommitment'
        '401':
          description: Missing or invalid token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '422':
          description: Any invalid entry rejects the whole batch
          content:
            application/json:
              examples:
                partial_failure:
                  value:
                    error: Invalid upload_ids
                    error_details:
                      - attribute: upload_ids[1]
                        message: upload session not found or expired
              schema:
                $ref: '#/components/schemas/ValidationErrorEnvelope'
      security:
        - PersonalAccessToken: []
components:
  schemas:
    AttachmentCommitmentRequest:
      type: object
      required:
        - upload_ids
      additionalProperties: false
      properties:
        upload_ids:
          type: array
          minItems: 1
          maxItems: 20
          description: >-
            upload_ids from POST /api/v1/attachments (purpose
            `rich_text_image`), each after its file was PUT to the upload_url.
            Finalized atomically: any invalid entry rejects the whole request,
            and already-processed entries are remembered so an identical retry
            does not duplicate them.
          items:
            type: string
            minLength: 1
            example: rich_text_image.ab12cd34ef567890
    AttachmentCommitment:
      type: object
      required:
        - attachments
      description: >-
        Finalize-step response: the stored rich-text images, in the same order
        as the request `upload_ids`. Embed each `image_url` into your rich-text
        HTML (e.g. `<img src="...">`).
      properties:
        attachments:
          type: array
          items:
            type: object
            required:
              - upload_id
              - id
              - image_url
              - file_name
            properties:
              upload_id:
                type: string
                description: Echo of the request entry this result belongs to.
              id:
                type: string
                description: Attachment id of the stored image.
              image_url:
                type: string
                format: uri
                description: >-
                  Public URL of the stored image; use it as the `<img>` src in
                  rich-text content.
              file_name:
                type: string
                description: Stored file name.
    ErrorEnvelope:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Error message
    ValidationErrorEnvelope:
      type: object
      required:
        - error
        - error_details
      description: >-
        Returned on model validation failures. error_details lists per-field
        errors.
      properties:
        error:
          type: string
          example: Name can't be blank
        error_details:
          type: array
          items:
            type: object
            required:
              - attribute
              - message
            properties:
              attribute:
                type: string
                example: name
              message:
                type: string
                example: can't be blank
  securitySchemes:
    PersonalAccessToken:
      type: http
      scheme: bearer
      bearerFormat: PAT
      description: >
        Personal Access Token prefixed with `fh_`. Sent as `Authorization:
        Bearer fh_xxx`.

        The scope required by each endpoint is listed in that endpoint's
        description.

````