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

# Submit a form

> Submits an Introw form (deal registration, lead share, onboarding, feedback, …) server-to-server. The payload is validated against the form's field definitions and runs the exact same pipeline as a partner filling in the form on the portal: CRM object automations, the form's acceptance flow, notifications, and timeline events.

Use this to register deals or share leads from your own systems (a partner-facing app, an internal tool, or another platform) without sending anyone to the form itself.

**Field ids.** `fields` is keyed by form field id. Field ids are opaque, generated identifiers (for example `qkzv8h2m4t6r1yc9pd3sxf70`). They are never human-readable names, and they are unique per form. Call `GET /api/v1/forms/{formId}/schema` for the exact list (every field id, its label, whether it is required, the value shape to send, and the allowed values of every picklist), or read it off the form's **Share → API** tab in Introw. Values are validated with the form's own rules; a payload that violates them (missing required fields, wrong formats) returns `422` with per-field details. Keys that don't match a form field are ignored.

**Picklists.** For a `DROPDOWN`, `DROPDOWN_MULTI` or `PARTNER_SELECT` field, send one of the option values the schema lists (join multiple with `;`). Option *labels* are accepted too and resolved to their value case-insensitively, so "Qualified to buy" and `qualifiedtobuy` both work, and a partner can be named rather than referenced by id. A value matching no option is passed through as sent, so an unrestricted picklist keeps accepting free text.

**Partner attribution.** Pass `partnerId` to relate the submission to a specific partner (the same guarantee as the form's partner-specific share link). It accepts either the Introw partner id or the partner's CRM external id, so you can key off whichever identifier your own systems already hold; an identifier that matches neither returns `404`. With only `email`, Introw makes a best effort to resolve the partner from the submitter, the same behaviour as the form's general share link.

**Status.** Forms without an acceptance flow auto-accept the submission and trigger their automation immediately (`AUTO_ACCEPTED`). Forms with an acceptance flow return `PENDING` until a teammate (or the AI acceptance agent) processes the submission.



## OpenAPI

````yaml /openapi.json post /api/v1/forms/{formId}/submissions
openapi: 3.1.0
info:
  title: Introw API
  version: '2026-05-20'
  description: >-
    Customer-facing API for Introw. Manage partners, delegate commission payout
    operations to your finance software, embed the partner portal in your
    product with pre-authenticated sessions, ingest affiliate conversions, and
    trigger on-demand CRM object syncs.
servers:
  - url: https://api.introw.io
  - url: https://api.staging.introw.io
security:
  - ApiKeyAuth: []
tags:
  - name: Partners
    description: >-
      Read and manage partners for the organisation associated with the
      authenticated API key.
  - name: Payouts
    description: >-
      Read and update commission payouts. Delegate approval, scheduling, and
      payment status to your ERP or accounts-payable stack.
  - name: Commission lines
    description: >-
      Create, read, update, decline, and detach commission line items. Lines can
      live in the pending pool or be attached to a payout.
  - name: Collaboration
    description: >-
      Post comments on partner portals, deals, tasks, form submissions, and
      payouts, with rich text, internal visibility, and thread replies. Comments
      trigger the same notifications, workflows, and CRM note sync as comments
      posted in the app.
  - name: Forms
    description: >-
      Submit Introw forms server-to-server. Register deals, share leads, or file
      any other form submission from your own systems; submissions run the exact
      same automation and acceptance pipeline as portal submissions. Read a
      form's field schema first to discover the field ids, value shapes, and
      allowed values to send.
  - name: Auth
    description: >-
      Create pre-authenticated partner portal sessions. Exchange a visitor email
      for a short-lived, single-use portal URL that you embed in an iframe
      inside your own product: one login, fully branded, no login screen for the
      partner.
  - name: Affiliate
    description: >-
      Ingest affiliate conversions from your backend or directly from the
      browser via affiliate.js. Every conversion is anchored to a signed,
      server-side click token (the first-party `_introw_aff` cookie),
      deduplicated, and mapped onto a campaign form submission, so attribution
      cannot be forged or double-counted.
  - name: CRM
    description: >-
      Trigger on-demand syncs of individual CRM objects. Introw already syncs
      every object automatically about every 15 minutes; use this only when a
      change must be reflected in Introw immediately, e.g. a workflow mutated
      the record and its result must persist in Introw right away.
paths:
  /api/v1/forms/{formId}/submissions:
    post:
      tags:
        - Forms
      summary: Submit a form
      description: >-
        Submits an Introw form (deal registration, lead share, onboarding,
        feedback, …) server-to-server. The payload is validated against the
        form's field definitions and runs the exact same pipeline as a partner
        filling in the form on the portal: CRM object automations, the form's
        acceptance flow, notifications, and timeline events.


        Use this to register deals or share leads from your own systems (a
        partner-facing app, an internal tool, or another platform) without
        sending anyone to the form itself.


        **Field ids.** `fields` is keyed by form field id. Field ids are opaque,
        generated identifiers (for example `qkzv8h2m4t6r1yc9pd3sxf70`). They are
        never human-readable names, and they are unique per form. Call `GET
        /api/v1/forms/{formId}/schema` for the exact list (every field id, its
        label, whether it is required, the value shape to send, and the allowed
        values of every picklist), or read it off the form's **Share → API** tab
        in Introw. Values are validated with the form's own rules; a payload
        that violates them (missing required fields, wrong formats) returns
        `422` with per-field details. Keys that don't match a form field are
        ignored.


        **Picklists.** For a `DROPDOWN`, `DROPDOWN_MULTI` or `PARTNER_SELECT`
        field, send one of the option values the schema lists (join multiple
        with `;`). Option *labels* are accepted too and resolved to their value
        case-insensitively, so "Qualified to buy" and `qualifiedtobuy` both
        work, and a partner can be named rather than referenced by id. A value
        matching no option is passed through as sent, so an unrestricted
        picklist keeps accepting free text.


        **Partner attribution.** Pass `partnerId` to relate the submission to a
        specific partner (the same guarantee as the form's partner-specific
        share link). It accepts either the Introw partner id or the partner's
        CRM external id, so you can key off whichever identifier your own
        systems already hold; an identifier that matches neither returns `404`.
        With only `email`, Introw makes a best effort to resolve the partner
        from the submitter, the same behaviour as the form's general share link.


        **Status.** Forms without an acceptance flow auto-accept the submission
        and trigger their automation immediately (`AUTO_ACCEPTED`). Forms with
        an acceptance flow return `PENDING` until a teammate (or the AI
        acceptance agent) processes the submission.
      operationId: submitForm
      parameters:
        - name: formId
          in: path
          required: true
          description: >-
            Identifier of the form, shown in the form's share dialog and in the
            form URL.
          schema:
            type: string
            minLength: 1
            description: >-
              Form identifier, shown in the form's share dialog and in the form
              URL.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubmitFormBody'
            example:
              email: partner.user@acme.example
              partnerId: ptn_01HVK6Y8Z8Q7J8J8J8J8J8J8J8
              fields:
                qkzv8h2m4t6r1yc9pd3sxf70: Jane
                w4n7bd1jy9es2rq6uh05lka3: jane@lead.example
                f6ptc93xoa1mzv8ke5rjn2y4: Lead Corp
              partnerObjects:
                - objectType: deal
                  objectId: '9876543210'
      responses:
        '201':
          description: Form was submitted successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormSubmissionResponse'
              example:
                data:
                  id: fsub_01HVK6Y8Z8Q7J8J8J8J8J8J8J8
                  formId: form_01HVK6Y8Z8Q7J8J8J8J8J8J8J8
                  status: AUTO_ACCEPTED
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '401':
          description: API key is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '402':
          description: >-
            Monthly API credit allowance reached. Credits reset at the start of
            the next calendar month.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
            retry-after:
              description: >-
                Seconds until the API credit allowance resets (the start of the
                next calendar month).
              schema:
                type: string
        '403':
          description: API key does not include the forms:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '404':
          description: >-
            Form was not found in the API key's organisation, or `partnerId`
            matched neither an Introw partner id nor a partner external id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '422':
          description: >-
            Request body failed validation, or field values violate the form's
            validation rules.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '429':
          description: Rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '500':
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
components:
  schemas:
    SubmitFormBody:
      type: object
      properties:
        fields:
          type: object
          propertyNames:
            type: string
          additionalProperties: {}
          description: >-
            Submitted values keyed by form field id. Field ids are opaque,
            generated identifiers (for example `qkzv8h2m4t6r1yc9pd3sxf70`),
            never human-readable names. Fetch them from `GET
            /api/v1/forms/{formId}/schema`, which lists every field id with its
            label, whether it is required, the value shape to send, and the
            allowed values of every picklist. Values that fail the form's
            validation rules return 422; keys that don't match a field are
            ignored.
        email:
          description: >-
            Email of the person submitting the form, when known. Used to resolve
            the submitter identity and, together with the form's identification
            automations, relate the submission to a partner.
          anyOf:
            - type: string
              format: email
              pattern: >-
                ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
            - type: string
              const: ''
        partnerId:
          description: >-
            Partner to relate the submission to. Accepts the Introw partner id
            or the partner's CRM external id. Returns 404 when it matches
            neither. When omitted, Introw makes a best effort to resolve the
            partner from the submitter email.
          type: string
          minLength: 1
        partnerObjects:
          description: >-
            CRM objects from the submitting partner's own CRM to link to the
            submission.
          type: array
          items:
            type: object
            properties:
              objectType:
                type: string
                minLength: 1
                description: CRM object type (for example `deal` or `contact`).
              objectId:
                type: string
                minLength: 1
                description: CRM object identifier.
            required:
              - objectType
              - objectId
            additionalProperties: false
      required:
        - fields
      additionalProperties: false
    FormSubmissionResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            id:
              type: string
              description: Form submission identifier.
            formId:
              type: string
              description: Identifier of the submitted form.
            status:
              type: string
              enum:
                - PENDING
                - ACCEPTED
                - AUTO_ACCEPTED
                - DECLINED
                - RETURNED
                - ERROR
              description: >-
                Submission review status. Forms without an acceptance flow
                auto-accept (`AUTO_ACCEPTED`) and run their automation
                immediately; forms with an acceptance flow start as `PENDING`
                until reviewed.
          required:
            - id
            - formId
            - status
          additionalProperties: false
      required:
        - data
      additionalProperties: false
    PublicApiError:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: Stable machine-readable error code.
            message:
              type: string
              description: Human-readable error message.
            details:
              description: Optional structured details for validation and debugging.
          required:
            - code
            - message
          additionalProperties: false
      required:
        - error
      additionalProperties: false
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: Introw API key shown once when the credential is created.

````