> ## 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.

# Get a form schema

> Returns the form's submittable fields: the field id to use as the payload key, the label, whether the field is required, the value shape to send, and (for picklists) the allowed values. Call this before `POST /api/v1/forms/{formId}/submissions` instead of copying field ids out of the Introw UI by hand.

**Field ids.** `id` is the key to use in the submission's `fields` map. Ids are opaque, generated identifiers (for example `qkzv8h2m4t6r1yc9pd3sxf70`), never human-readable names, and unique per form. They are stable: a field keeps its id across form edits.

**Value shapes.** `dataType` is the one field to branch on: it tells you what to send, whether that is a string, a number, a `YYYY-MM-DD` date, a picklist value, or a file URL. Fields that differ only in how the portal draws them (a single-line text box versus a text area) share a data type; only the kinds that behave differently keep their own: `PARTNER_SELECT` picks an Introw partner, `QUOTE_SELECTOR` an Introw quote, and `BATCH_UPLOAD` carries no value at all (it is the portal's bulk-CSV control, so over the API you send one request per submission).

**Options.** `options` is present exactly when `dataType` is `DROPDOWN`, `DROPDOWN_MULTI`, or `PARTNER_SELECT`, and is resolved live: from the CRM picklist behind the field, the pipeline stages of the form's own CRM automation, the organisation's synced CRM owners, the partner audience of a partner-select field, or the builder's preset list. Options limited by the builder are already excluded. **Prefer sending `value`.** A `label` is also accepted and resolved to its value on submission (case-insensitively), so a payload built from human-readable names still lands correctly in the CRM; a value matching no option is passed through untouched rather than rejected, which is what keeps unrestricted picklists working. Send `DROPDOWN_MULTI` values joined with `;`. An empty array means the option set could not be resolved (for example the CRM property has not synced yet), which is a configuration problem to fix in Introw rather than something to work around in the payload.

Fetch this whenever the form may have changed rather than caching it indefinitely: adding a required field, or changing a picklist in the CRM, changes what a valid submission looks like.



## OpenAPI

````yaml /openapi.json get /api/v1/forms/{formId}/schema
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}/schema:
    get:
      tags:
        - Forms
      summary: Get a form schema
      description: >-
        Returns the form's submittable fields: the field id to use as the
        payload key, the label, whether the field is required, the value shape
        to send, and (for picklists) the allowed values. Call this before `POST
        /api/v1/forms/{formId}/submissions` instead of copying field ids out of
        the Introw UI by hand.


        **Field ids.** `id` is the key to use in the submission's `fields` map.
        Ids are opaque, generated identifiers (for example
        `qkzv8h2m4t6r1yc9pd3sxf70`), never human-readable names, and unique per
        form. They are stable: a field keeps its id across form edits.


        **Value shapes.** `dataType` is the one field to branch on: it tells you
        what to send, whether that is a string, a number, a `YYYY-MM-DD` date, a
        picklist value, or a file URL. Fields that differ only in how the portal
        draws them (a single-line text box versus a text area) share a data
        type; only the kinds that behave differently keep their own:
        `PARTNER_SELECT` picks an Introw partner, `QUOTE_SELECTOR` an Introw
        quote, and `BATCH_UPLOAD` carries no value at all (it is the portal's
        bulk-CSV control, so over the API you send one request per submission).


        **Options.** `options` is present exactly when `dataType` is `DROPDOWN`,
        `DROPDOWN_MULTI`, or `PARTNER_SELECT`, and is resolved live: from the
        CRM picklist behind the field, the pipeline stages of the form's own CRM
        automation, the organisation's synced CRM owners, the partner audience
        of a partner-select field, or the builder's preset list. Options limited
        by the builder are already excluded. **Prefer sending `value`.** A
        `label` is also accepted and resolved to its value on submission
        (case-insensitively), so a payload built from human-readable names still
        lands correctly in the CRM; a value matching no option is passed through
        untouched rather than rejected, which is what keeps unrestricted
        picklists working. Send `DROPDOWN_MULTI` values joined with `;`. An
        empty array means the option set could not be resolved (for example the
        CRM property has not synced yet), which is a configuration problem to
        fix in Introw rather than something to work around in the payload.


        Fetch this whenever the form may have changed rather than caching it
        indefinitely: adding a required field, or changing a picklist in the
        CRM, changes what a valid submission looks like.
      operationId: getFormSchema
      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.
      responses:
        '200':
          description: Form schema was resolved.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormSchemaResponse'
              example:
                data:
                  id: form_01HVK6Y8Z8Q7J8J8J8J8J8J8J8
                  name: Register a deal
                  fields:
                    - id: qkzv8h2m4t6r1yc9pd3sxf70
                      label: Deal name
                      isRequired: true
                      dataType: STRING
                      crmObject: DEAL
                      crmPropertyName: dealname
                    - id: w4n7bd1jy9es2rq6uh05lka3
                      label: Contact email
                      isRequired: true
                      dataType: EMAIL
                      crmObject: CONTACT
                      crmPropertyName: email
                    - id: f6ptc93xoa1mzv8ke5rjn2y4
                      label: Expected deal size
                      isRequired: false
                      dataType: NUMBER
                      crmObject: DEAL
                      crmPropertyName: amount
                    - id: b2rk8wq5vn3ty7uc1md9zjx6
                      label: Deal stage
                      isRequired: true
                      dataType: DROPDOWN
                      crmObject: DEAL
                      crmPropertyName: dealstage
                      options:
                        - label: Appointment scheduled
                          value: appointmentscheduled
                        - label: Qualified to buy
                          value: qualifiedtobuy
                    - id: m8vs4kx1zt7ry2qc9nd5jwb3
                      label: Partner
                      isRequired: true
                      dataType: PARTNER_SELECT
                      options:
                        - label: Acme Partners
                          value: ptn_01HVK6Y8Z8Q7J8J8J8J8J8J8J8
          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 includes neither the forms:read nor 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.
          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 while resolving the form schema.
          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:
    FormSchemaResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            id:
              type: string
              description: Form identifier.
            name:
              type: string
              description: Form name.
            fields:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                    description: >-
                      Field id: the key to use in the `fields` map when
                      submitting. Opaque and unique per form (for example
                      `qkzv8h2m4t6r1yc9pd3sxf70`).
                  label:
                    type: string
                    description: >-
                      Field label as configured in the form builder, in the
                      organisation's default language. Falls back to the field
                      id when the field has no label.
                  isRequired:
                    type: boolean
                    description: >-
                      Whether a submission is rejected with `422` when this
                      field is missing or empty.
                  dataType:
                    type: string
                    enum:
                      - STRING
                      - EMAIL
                      - NUMBER
                      - BOOLEAN
                      - DATE
                      - DATETIME
                      - DROPDOWN
                      - DROPDOWN_MULTI
                      - PARTNER_SELECT
                      - FILE_URL
                      - CRM_OBJECT_ID
                      - QUOTE_SELECTOR
                      - BATCH_UPLOAD
                    description: >-
                      Value to send for this field:

                      - `STRING`: any string.

                      - `EMAIL`: a valid email address.

                      - `NUMBER`: a number, or a numeric string (thousand
                      separators and currency symbols are tolerated).

                      - `BOOLEAN`: `true` or `false`.

                      - `DATE`: a `YYYY-MM-DD` date (other common formats are
                      normalised).

                      - `DATETIME`: an ISO 8601 timestamp.

                      - `DROPDOWN`: exactly one of the field's
                      `options[].value`.

                      - `DROPDOWN_MULTI`: one or more of the field's
                      `options[].value`, joined with `;`.

                      - `PARTNER_SELECT`: the id of one of the partners in
                      `options[].value`; the field picks an Introw partner
                      rather than a CRM value.

                      - `FILE_URL`: a publicly reachable URL of an
                      already-uploaded file; Introw stores it as an asset on the
                      submission.

                      - `CRM_OBJECT_ID`: the CRM record id of an object of type
                      `crmObjectType`.

                      - `QUOTE_SELECTOR`: the id of an Introw quote. Quote
                      selection is a portal flow; prefer sending partners to the
                      form itself for these.

                      - `BATCH_UPLOAD`: not a value-carrying field. On the
                      portal it turns one form into many submissions from an
                      uploaded CSV; over the API, send one request per
                      submission and omit this key.
                  crmObject:
                    description: >-
                      CRM object type this field writes to, when it is mapped to
                      a CRM property.
                    type: string
                  crmPropertyName:
                    description: >-
                      CRM property this field writes to, when it is mapped to
                      one.
                    type: string
                  crmObjectType:
                    description: >-
                      For a `CRM_OBJECT_ID` field: the CRM object type whose
                      record id the field expects.
                    type: string
                  options:
                    description: >-
                      Allowed values, resolved at request time from the CRM
                      picklist, pipeline stages, synced owners, partner
                      audience, or the builder's own preset list. Present
                      exactly when `dataType` is `DROPDOWN`, `DROPDOWN_MULTI`,
                      or `PARTNER_SELECT`; an empty array means the option set
                      could not be resolved (for example the CRM property is not
                      synced yet). Prefer sending `value`; a `label` is also
                      accepted and resolved to its value (case-insensitively) on
                      submission.
                    type: array
                    items:
                      type: object
                      properties:
                        label:
                          type: string
                          description: Option label as shown on the form.
                        value:
                          type: string
                          description: Value to send for this option.
                      required:
                        - label
                        - value
                      additionalProperties: false
                required:
                  - id
                  - label
                  - isRequired
                  - dataType
                additionalProperties: false
              description: Submittable fields, in the order they appear on the form.
          required:
            - id
            - name
            - fields
          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.

````