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

# Submit a form

> Send answers to a published Nautilus form from your own frontend

Submit answers using the form's saved field IDs. This is a public endpoint: no tenant API key or staff session is required. Include `_password` if the form is password-protected.

Follow [Build a headless form](/guides/headless-forms) to copy your endpoint, field IDs, consent text, and optional JavaScript from **Share**.

<RequestExample>
  ```bash JSON theme={null}
  curl -X POST "https://app.nautilus.co/api/forms/FORM_ID/submissions" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -d '{
      "EMAIL_FIELD_ID": "customer@example.com",
      "_gotcha": ""
    }'
  ```

  ```bash Form data theme={null}
  curl -X POST "https://app.nautilus.co/api/forms/FORM_ID/submissions" \
    -H "Accept: application/json" \
    --data-urlencode "EMAIL_FIELD_ID=customer@example.com" \
    --data-urlencode "_gotcha="
  ```

  ```bash HTML redirect theme={null}
  curl -X POST "https://app.nautilus.co/api/forms/FORM_ID/submissions" \
    -H "Accept: text/html" \
    -H "Origin: https://your-website.example" \
    --data-urlencode "EMAIL_FIELD_ID=customer@example.com" \
    --data-urlencode "_gotcha=" \
    --data-urlencode "_redirect=https://your-website.example/thank-you"
  ```
</RequestExample>

<ResponseExample>
  ```json Saved submission theme={null}
  {
    "ok": true,
    "submissionId": "SUBMISSION_ID",
    "title": "Thank you!",
    "message": "Your form has been submitted successfully."
  }
  ```

  ```json Invalid answer theme={null}
  {
    "ok": false,
    "code": "validation_error",
    "error": "Check the form fields and try again.",
    "fieldErrors": {
      "EMAIL_FIELD_ID": "Enter a valid value for Email."
    }
  }
  ```

  ```json Filled honeypot theme={null}
  {
    "ok": true,
    "message": "Your form has been submitted successfully."
  }
  ```
</ResponseExample>

## Request rules

* Send a flat JSON object, URL-encoded form data, or text-only multipart data. Do not nest answers inside `data`.
* Replace example field names with the exact saved IDs. Required fields and available choices depend on the form.
* Include `_gotcha` and leave it empty. A filled trap receives an acknowledgment without creating a submission or triggering side effects.
* Keep the complete body at or below 65,536 bytes. Files are not supported.
* Supply `_redirect` only when its destination has the same origin as the request's `Origin` header.

JSON requests always receive JSON. Native requests that accept HTML receive an HTML confirmation or a `303` redirect. JSON confirmations include `redirectUrl` when supplied and validated.

## Public access

The route accepts cross-origin submissions. `OPTIONS` returns `204` with `Access-Control-Allow-Origin: *`, allowed methods `POST, OPTIONS`, and allowed headers `Content-Type, Accept`.

The published form ID grants submission access only. Reading configuration or saved submissions through `/api/v1/forms` still requires a tenant API key. A `401` from this submission endpoint means the form password is missing or incorrect; adding an API key does not replace that password.

## Confirmation and availability

Only a saved submission includes `submissionId`. The receipt confirms persistence, not completion of every notification or automation. Notifications are scheduled after the response, so a slow email provider does not delay confirmation. Do not automatically retry an uncertain request because no request idempotency key is supported.

Email verification, POS enrollment, file-upload fields, and unsupported saved field definitions return `409 hosted_form_required`. Null optional field settings are treated as omitted; malformed definitions and comparison rules remain unsupported. Use the hosted flow when the sharing panel indicates it is required.

See the guide for [answer formats](/guides/headless-forms#map-your-answers), [tracking limits](/guides/headless-forms#preserve-attribution), and [spam protection](/guides/headless-forms#spam-protection-and-limits).


## OpenAPI

````yaml POST /api/forms/{formId}/submissions
openapi: 3.1.0
info:
  title: Nautilus Public API
  version: 1.0.0
  description: >-
    The canonical /api/v1 data endpoints are tenant-scoped and use a Clerk
    tenant API key as a Bearer token. The key's organizationId is the sole
    tenant scope; caller-supplied organization IDs are not accepted. Detail
    reads return 404 for both missing and cross-tenant records. Canonical
    collections use the standard envelope and endpoint-bound cursor pagination.
    Timestamps are UTC ISO 8601 values and monetary amounts ending in Cents are
    integer cents. Responses use narrow serializers that exclude credentials and
    internal provider or workflow data. GET /api/v1/openapi.json and the
    headless form submission endpoint do not require an API key. Headless
    submissions use a published form ID and its configured form password, grant
    submission access only, and return the separate receipt/error shapes
    documented on that operation. Use headless submissions for published forms
    supported by headless intake. Copy the endpoint and saved field IDs from
    Share > Use your own form; hosted-only form requirements remain unchanged.
servers:
  - url: https://app.nautilus.co
security:
  - tenantApiKey: []
tags:
  - name: Contract
  - name: Cancellations
  - name: Sites
  - name: Products
  - name: Checkout links
  - name: Purchases
  - name: Contact lists
  - name: Contacts
  - name: Marketing
  - name: Forms
  - name: Vouchers
  - name: Links
  - name: Automations
  - name: Surveys
  - name: Cases
  - name: Reviews
  - name: POS
    description: >-
      Point-of-sale data for the organization that owns the API key. The POS is
      resolved server-side from that organization; requests never name or select
      a provider.
  - name: Analytics
  - name: Messaging
  - name: Legacy
    description: Deprecated noncanonical endpoints retained for compatibility.
paths:
  /api/forms/{formId}/submissions:
    post:
      tags:
        - Forms
      summary: Submit a form
      description: >-
        Use published forms supported by headless intake. Copy the endpoint and
        saved field IDs from your form's Share > Use your own form panel.


        Submit a form rendered on your own website without an iframe or a tenant
        API key. Use the published form ID, saved field IDs, required empty
        _gotcha, and the form password when configured. Supports cross-origin
        requests without credentials. JSON bodies always receive JSON; other
        formats receive HTML only when Accept includes text/html and does not
        include application/json.


        The receipt confirms persistence, not completed notification delivery or
        workflow execution. Notifications are scheduled after the response, so a
        slow email provider does not delay confirmation. There is no request
        idempotency key; do not automatically retry an uncertain request. Basic
        spam protection uses the honeypot; no new rate limiting or CAPTCHA is
        enabled.


        See [Build a headless form](/guides/headless-forms) for setup, answer
        types, phone normalization, attribution limits, and hosted-only
        boundaries.
      operationId: submitHeadlessForm
      parameters:
        - name: formId
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 200
            pattern: ^[a-zA-Z0-9_-]+$
          description: Published form ID copied from Share. It permits submission only.
        - name: Origin
          in: header
          required: false
          schema:
            type: string
          description: >-
            Submitting website origin. Required when _redirect is supplied; the
            redirect destination must match its scheme, host, and port.
      requestBody:
        required: true
        description: >-
          Flat saved field IDs and control fields. Include every required
          visible answer. For multipart, let the browser generate the
          Content-Type boundary; files are not supported. HTML checkbox groups
          use repeated field names.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HeadlessFormSubmission'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/HeadlessFormSubmission'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/HeadlessFormSubmission'
      responses:
        '200':
          description: >-
            Saved-submission receipt, or ordinary acknowledgment for a filled
            honeypot. Only a saved submission includes submissionId.
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
                const: '*'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HeadlessFormReceipt'
              examples:
                saved:
                  summary: Saved submission
                  value:
                    ok: true
                    submissionId: SUBMISSION_ID
                    title: Thank you!
                    message: Your form has been submitted successfully.
                honeypot:
                  summary: Filled honeypot; no writes
                  value:
                    ok: true
                    message: Your form has been submitted successfully.
            text/html:
              schema:
                type: string
                description: HTML confirmation or error page for native browser requests.
        '303':
          description: >-
            Native HTML acknowledgment redirects to the validated same-origin
            thank-you URL. JSON requests receive redirectUrl in a 200 response
            instead.
          headers:
            Location:
              required: true
              schema:
                type: string
                format: uri
              description: Validated absolute thank-you URL.
        '400':
          description: Malformed or missing body; JSON must be an object.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HeadlessFormError'
            text/html:
              schema:
                type: string
                description: HTML confirmation or error page for native browser requests.
        '401':
          description: >-
            Form password is missing or incorrect. Tenant API keys do not
            replace form passwords.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HeadlessFormError'
            text/html:
              schema:
                type: string
                description: HTML confirmation or error page for native browser requests.
        '404':
          description: Form ID is invalid, or the form is missing or unpublished.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HeadlessFormError'
            text/html:
              schema:
                type: string
                description: HTML confirmation or error page for native browser requests.
        '409':
          description: >-
            hosted_form_required: email verification, POS enrollment,
            file-upload fields, or an unsupported saved field definition. Null
            optional field settings are treated as omitted; malformed
            definitions and comparison rules remain unsupported.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HeadlessFormError'
            text/html:
              schema:
                type: string
                description: HTML confirmation or error page for native browser requests.
        '413':
          description: >-
            Complete request body exceeds 65536 bytes, including streamed data
            and multipart overhead.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HeadlessFormError'
            text/html:
              schema:
                type: string
                description: HTML confirmation or error page for native browser requests.
        '415':
          description: >-
            Unsupported content type or a binary/file value. Only JSON,
            URL-encoded forms, and text-only multipart are accepted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HeadlessFormError'
            text/html:
              schema:
                type: string
                description: HTML confirmation or error page for native browser requests.
        '422':
          description: >-
            Invalid answers, missing _gotcha, repeated or invalid controls,
            invalid redirect, or the configured per-contact submission limit was
            reached.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HeadlessFormError'
              example:
                ok: false
                code: validation_error
                error: Check the form fields and try again.
                fieldErrors:
                  EMAIL_FIELD_ID: Enter a valid value for Email.
            text/html:
              schema:
                type: string
                description: HTML confirmation or error page for native browser requests.
        '500':
          description: >-
            Submission processing failed. A network or server failure can leave
            persistence uncertain; do not automatically retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HeadlessFormError'
            text/html:
              schema:
                type: string
                description: HTML confirmation or error page for native browser requests.
      security: []
components:
  schemas:
    HeadlessFormSubmission:
      type: object
      description: >-
        Flat answers keyed by the published form's saved field IDs, plus the
        controls below. There is no data wrapper. Only IDs belonging to that
        form are accepted; requiredness, choices, and conditional visibility
        come from its saved definition. Total body size is limited to 65536
        bytes, including multipart overhead.
      required:
        - _gotcha
      properties:
        _gotcha:
          type: string
          maxLength: 10000
          default: ''
          description: >-
            Required hidden bot-trap field. Leave it empty for real submissions.
            A nonempty value receives an acknowledgment without a submissionId
            and creates no records or downstream effects.
        _password:
          type: string
          format: password
          maxLength: 1024
          description: >-
            The form password, required only when the published form is
            password-protected. This is not a tenant API key.
        _redirect:
          type: string
          maxLength: 2000
          description: >-
            Optional thank-you URL, either relative to Origin or an absolute
            HTTP(S) URL with the same scheme, host, and port. Requires the
            Origin header. Native HTML receives a 303 redirect; JSON receives
            redirectUrl.
        _utm_source:
          type: string
          description: >-
            Optional utm_source attribution. Strings are trimmed to 450
            characters when stored. Empty or malformed values are ignored
            without rejecting valid answers; the complete request must still fit
            within 65536 bytes.
        _utm_medium:
          type: string
          description: >-
            Optional utm_medium attribution. Strings are trimmed to 450
            characters when stored. Empty or malformed values are ignored
            without rejecting valid answers; the complete request must still fit
            within 65536 bytes.
        _utm_campaign:
          type: string
          description: >-
            Optional utm_campaign attribution. Strings are trimmed to 450
            characters when stored. Empty or malformed values are ignored
            without rejecting valid answers; the complete request must still fit
            within 65536 bytes.
        _utm_term:
          type: string
          description: >-
            Optional utm_term attribution. Strings are trimmed to 450 characters
            when stored. Empty or malformed values are ignored without rejecting
            valid answers; the complete request must still fit within 65536
            bytes.
        _utm_content:
          type: string
          description: >-
            Optional utm_content attribution. Strings are trimmed to 450
            characters when stored. Empty or malformed values are ignored
            without rejecting valid answers; the complete request must still fit
            within 65536 bytes.
        _gclid:
          type: string
          description: >-
            Optional gclid attribution. Strings are trimmed to 450 characters
            when stored. Empty or malformed values are ignored without rejecting
            valid answers; the complete request must still fit within 65536
            bytes.
        _gbraid:
          type: string
          description: >-
            Optional gbraid attribution. Strings are trimmed to 450 characters
            when stored. Empty or malformed values are ignored without rejecting
            valid answers; the complete request must still fit within 65536
            bytes.
        _wbraid:
          type: string
          description: >-
            Optional wbraid attribution. Strings are trimmed to 450 characters
            when stored. Empty or malformed values are ignored without rejecting
            valid answers; the complete request must still fit within 65536
            bytes.
        _fbclid:
          type: string
          description: >-
            Optional fbclid attribution. Strings are trimmed to 450 characters
            when stored. Empty or malformed values are ignored without rejecting
            valid answers; the complete request must still fit within 65536
            bytes.
        _referrer:
          type: string
          description: >-
            Optional referrer attribution. Strings are trimmed to 450 characters
            when stored. Empty or malformed values are ignored without rejecting
            valid answers; the complete request must still fit within 65536
            bytes.
        _landing_url:
          type: string
          description: >-
            Optional landing_url attribution. Strings are trimmed to 450
            characters when stored. Empty or malformed values are ignored
            without rejecting valid answers; the complete request must still fit
            within 65536 bytes.
      additionalProperties:
        description: >-
          Answer to a saved field ID. Strings cover text, email, phone, numeric
          text, dates, and choices; arrays represent multiple checkbox choices.
          Null is treated as an empty answer and remains subject to
          required-field validation. Nested objects and uploaded files are
          rejected.
        anyOf:
          - type: string
            maxLength: 10000
          - type: number
          - type: boolean
          - type: array
            items:
              type: string
            maxItems: 100
          - type: 'null'
      example:
        EMAIL_FIELD_ID: customer@example.com
        _gotcha: ''
    HeadlessFormReceipt:
      type: object
      additionalProperties: false
      required:
        - ok
        - message
      description: >-
        Acknowledgment of a saved submission or a filled honeypot. Only a saved
        submission has submissionId. The receipt does not include answers,
        contact records, or notification recipients and does not guarantee
        completed downstream delivery.
      properties:
        ok:
          type: boolean
          const: true
        submissionId:
          type: string
          description: Identifier of the saved submission. Absent for a filled honeypot.
        title:
          type: string
          description: Configured success title, returned for a saved submission.
        message:
          type: string
          description: Configured success message or ordinary honeypot acknowledgment.
        redirectUrl:
          type: string
          format: uri
          description: >-
            Validated absolute thank-you URL, when requested. The JSON client
            decides when to navigate.
    HeadlessFormError:
      type: object
      additionalProperties: false
      required:
        - ok
        - code
        - error
      properties:
        ok:
          type: boolean
          const: false
        code:
          type: string
          description: >-
            Machine-readable failure code, such as validation_error,
            hosted_form_required, or invalid_redirect.
        error:
          type: string
          description: Explanation suitable for displaying to the submitter.
        fieldErrors:
          type: object
          additionalProperties:
            type: string
          description: Validation messages keyed by saved field ID, when applicable.
  securitySchemes:
    tenantApiKey:
      type: http
      scheme: bearer
      bearerFormat: Clerk tenant API key
      description: >-
        Clerk tenant API key. The organizationId encoded by the key is the exact
        and only tenant scope.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.