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

# Set a fill-in form's questions

> Sets the questions of a fill-in form (`createsAccount: false`), in the order given. The change is live immediately.

Resend every question you are keeping **with its `id`**, and list the ones to delete in `removeQuestionIds`. A question is never deleted by being left out: a list that is missing an existing question is a 400 (`FORM_QUESTION_OMITTED`) naming it. The ids matter because answers are stored against them, so a question resent without its id is a new question and the answers already given stop being linked to it. Deleting a question keeps past submissions readable with the wording they were asked under.

A sign-up form has no questions of its own (`FORM_QUESTIONS_FILL_IN_ONLY`).




## OpenAPI

````yaml /openapi-platform.json put /v1/platform/forms/{id}/questions
openapi: 3.0.0
info:
  title: 1club Platform API
  version: 1.0.0
  description: >-
    The 1club Platform API lets you programmatically access and manage your
    organization's data.


    ## Official API Contract


    This documentation is the official source of truth for the 1club Platform
    API.

    If an integration relies on undocumented endpoints, fields, response shapes,
    or internal behavior outside this spec, we can't guarantee backward
    compatibility.

    Build against what's documented here to stay stable as the platform evolves.


    ## Authentication


    All requests require a customer API key passed as a Bearer token:


    ```

    Authorization: Bearer 1club_sk_live_...

    ```


    Generate API keys from the admin portal under **Settings > API Tokens**.

    The key is tied to your organization - all responses are scoped to your
    org's data.


    ## Rate Limiting


    - **100 requests per minute** per API key

    - When exceeded, the API returns `429 Too Many Requests` with a
    `Retry-After` header

    - Rate limit headers are included in every response:
      - `X-RateLimit-Limit` - max requests per window
      - `X-RateLimit-Remaining` - requests remaining
      - `X-RateLimit-Reset` - seconds until the window resets

    ## Errors


    | Status | Meaning |

    |--------|---------|

    | `400` | Invalid request parameters |

    | `401` | Missing or invalid API key |

    | `404` | Resource not found (or doesn't belong to your organization) |

    | `429` | Rate limit exceeded |

    | `500` | Internal server error |
  contact:
    name: 1club API Support
    email: support@1club.ai
servers:
  - url: https://api.1club.ai
    description: Production API
security:
  - customerApiAuth: []
tags: []
paths:
  /v1/platform/forms/{id}/questions:
    put:
      tags:
        - Forms
      summary: Set a fill-in form's questions
      description: >
        Sets the questions of a fill-in form (`createsAccount: false`), in the
        order given. The change is live immediately.


        Resend every question you are keeping **with its `id`**, and list the
        ones to delete in `removeQuestionIds`. A question is never deleted by
        being left out: a list that is missing an existing question is a 400
        (`FORM_QUESTION_OMITTED`) naming it. The ids matter because answers are
        stored against them, so a question resent without its id is a new
        question and the answers already given stop being linked to it. Deleting
        a question keeps past submissions readable with the wording they were
        asked under.


        A sign-up form has no questions of its own
        (`FORM_QUESTIONS_FILL_IN_ONLY`).
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: integer
          description: 1club form id
        - in: header
          name: Idempotency-Key
          schema:
            type: string
            maxLength: 255
          description: >-
            Retry-safe key; a repeat of the same request replays the first
            response
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlatformFormQuestionsPut'
      responses:
        '200':
          description: The form, with its new questions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformForm'
        '400':
          $ref: '#/components/responses/PlatformBadRequest'
        '401':
          $ref: '#/components/responses/PlatformUnauthorized'
        '403':
          description: API key is missing the required `forms:write` scope
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformError'
        '404':
          $ref: '#/components/responses/PlatformNotFound'
        '429':
          $ref: '#/components/responses/PlatformRateLimited'
      security:
        - customerApiAuth: []
components:
  schemas:
    PlatformFormQuestionsPut:
      type: object
      required:
        - questions
      additionalProperties: false
      properties:
        questions:
          type: array
          maxItems: 100
          items:
            $ref: '#/components/schemas/PlatformFormQuestionWrite'
          description: Every question the form should ask, in order.
        removeQuestionIds:
          type: array
          items:
            type: integer
          description: >-
            Existing questions to delete. Every existing question must be either
            resent by id or listed here (`FORM_QUESTION_OMITTED`).
    PlatformForm:
      type: object
      description: 'A form: a page people fill in, live at `publicUrl`.'
      properties:
        id:
          type: integer
        slug:
          type: string
          description: The last part of `publicUrl`. Fixed once the form exists.
        createsAccount:
          type: boolean
          description: >-
            true for a sign-up form (registers the person, can sell); false for
            a fill-in form (collects answers). Fixed once the form exists.
        publicUrl:
          type: string
          nullable: true
          description: >-
            Where the form is filled in, on the organization's own website or
            member portal. It is live: anyone with the link can use it. null
            when the organization has neither switched on, so nothing can open
            the form until one is.
        name:
          type: string
          maxLength: 200
          description: Internal name, shown to staff in the admin portal.
        title:
          type: string
          nullable: true
          maxLength: 200
          description: >-
            Heading at the top of the form. Blank or null falls back to the
            wizard's own wording.
        subtitle:
          type: string
          nullable: true
          maxLength: 200
          description: >-
            Sub-heading under the title. Blank or null falls back to the
            wizard's own wording.
        planStepTitle:
          type: string
          nullable: true
          maxLength: 200
          description: >-
            Heading above the plan picker (sign-up forms). Blank or null falls
            back to the wizard's own wording.
        classTypeStepTitle:
          type: string
          nullable: true
          maxLength: 200
          description: >-
            Heading above the class-type picker (sign-up forms). Blank or null
            falls back to the wizard's own wording.
        planIds:
          type: array
          items:
            type: integer
          maxItems: 100
          description: >-
            Sign-up forms only. Plans the form sells, from GET
            /v1/platform/plans. Each must be active, publicly visible and not a
            season plan (`FORM_PLAN_NOT_SELLABLE` names each refused id and
            why). When any are set, the person must choose one to finish.
        classTypeIds:
          type: array
          items:
            type: integer
          maxItems: 100
          description: >-
            Sign-up forms only. Class types offered as an interest, from GET
            /v1/platform/class-types. Nothing is booked.
        productIds:
          type: array
          items:
            type: integer
          maxItems: 100
          description: >-
            Sign-up forms only. Products the form sells, from GET
            /v1/platform/products. Each must be active, publicly visible and
            sold as a product (`FORM_PRODUCT_NOT_SELLABLE`).
        registrationFor:
          type: string
          enum:
            - individual
            - child
            - both
          description: >-
            Who the form registers: the person filling it in, only their
            children, or both.
        signatureMethod:
          type: string
          enum:
            - draw
            - type
            - check
          description: 'How documents are signed: drawn, typed name, or a checkbox.'
        collectsDocuments:
          type: boolean
          description: >-
            Whether the form asks for the organization's documents and a
            signature. Which documents is set by the organization's document
            rules.
        requiresAuthentication:
          type: boolean
          description: >-
            Fill-in forms only: the person must be signed in to fill it in.
            Refused on a sign-up form (`FORM_AUTHENTICATION_FILL_IN_ONLY`).
        combineSelectionAndDetails:
          type: boolean
          description: >-
            Put the "choose what to buy" step and the details step on one page.
            Needs a form that sells something (`FORM_NOTHING_TO_COMBINE`).
        displayMode:
          type: string
          enum:
            - page
            - dialog
          description: >-
            How a link to the form opens: as its own page, or as a dialog over
            the page it was opened from.
        videoUrl:
          type: string
          nullable: true
          maxLength: 500
          description: >-
            A Cloudinary video URL from the media library, shown as a step
            before review. null removes the video step.
        videoRequired:
          type: boolean
          description: >-
            The person cannot continue until the video has played to the end.
            Needs a video (`FORM_VIDEO_REQUIRED_WITHOUT_VIDEO`).
        videoTitle:
          type: string
          nullable: true
          maxLength: 200
          description: >-
            Heading above the video. Blank or null falls back to the wizard's
            own wording.
        fieldOverrides:
          type: object
          nullable: true
          additionalProperties: false
          description: >-
            Fill-in forms only (`FORM_FIELD_OVERRIDES_FILL_IN_ONLY`; an empty
            object is accepted and ignored on a sign-up form, which asks the
            organization's own sign-up fields instead). Which built-in contact
            fields the form asks: `required` asks it, `hidden` does not. A
            fill-in form cannot ask one optionally. `address` asks the whole
            postal address. On update the map is merged: the fields you send
            change and the rest stay as they are, so send `hidden` to stop
            asking one. null clears every override.
          properties:
            firstName:
              type: string
              enum:
                - required
                - hidden
            lastName:
              type: string
              enum:
                - required
                - hidden
            email:
              type: string
              enum:
                - required
                - hidden
            phone:
              type: string
              enum:
                - required
                - hidden
            dateOfBirth:
              type: string
              enum:
                - required
                - hidden
            gender:
              type: string
              enum:
                - required
                - hidden
            address:
              type: string
              enum:
                - required
                - hidden
        translations:
          type: object
          nullable: true
          additionalProperties:
            type: object
            additionalProperties:
              type: string
          description: >-
            Per-locale wording, as `{ "es": { "title": "..." } }`. Replaces the
            whole map. Translatable fields: title, subtitle, planStepTitle,
            classTypeStepTitle, videoTitle; any other key is refused
            (`INVALID_TRANSLATIONS`).
        packageIds:
          type: array
          items:
            type: integer
          description: 'Read-only here: packages are added in the admin portal.'
        submissionCount:
          type: integer
          description: >-
            How many people have completed it. The answers are not served by
            this API.
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        questions:
          type: array
          items:
            $ref: '#/components/schemas/PlatformFormQuestion'
          description: In order. On a single-form read and on every write; not in the list.
    PlatformError:
      type: object
      properties:
        error:
          type: string
        code:
          type: string
          description: >-
            A stable, machine-readable reason, when the error has one (e.g.
            `CONTACT_EXISTS`).
        details:
          type: object
          additionalProperties: true
          description: >-
            Structured values behind the error. A 409 naming an existing record
            carries its id here, e.g. `contactId`.
    PlatformFormQuestionWrite:
      type: object
      required:
        - label
        - type
        - required
      additionalProperties: false
      properties:
        id:
          type: integer
          description: >-
            An existing question of this form, to keep it. Leave out for a new
            question.
        label:
          type: string
          maxLength: 300
        type:
          type: string
          enum:
            - text
            - textarea
            - yes_no
            - select
            - boolean
            - date
            - number
            - email
            - phone
            - url
            - time
        required:
          type: boolean
          description: 'Whether it must be answered. Always sent: there is no default.'
        placeholder:
          type: string
          nullable: true
          maxLength: 200
        helpText:
          type: string
          nullable: true
          maxLength: 500
        sectionHeading:
          type: string
          nullable: true
          maxLength: 200
        options:
          type: array
          nullable: true
          maxItems: 50
          items:
            type: string
            maxLength: 200
          description: >-
            Required for `select`. Not translatable: the choice is also the
            stored answer.
        contactCustomFieldId:
          type: integer
          nullable: true
          description: >-
            Keep or remove an existing link to a member profile field: send the
            value read from GET (or leave it out) to keep it, or null to remove
            it. Setting a new link is done in the admin portal
            (`FORM_QUESTION_LINK_NOT_SETTABLE`).
        translations:
          type: object
          nullable: true
          additionalProperties:
            type: object
            additionalProperties:
              type: string
          description: >-
            Per-locale wording. Translatable fields: label, placeholder,
            helpText, sectionHeading.
    PlatformFormQuestion:
      type: object
      description: >-
        One question on a fill-in form, as read. Resend it with its `id` to keep
        it.
      properties:
        id:
          type: integer
          description: 'Keep this: answers are stored against it.'
        label:
          type: string
        type:
          type: string
          enum:
            - text
            - textarea
            - yes_no
            - select
            - boolean
            - date
            - number
            - email
            - phone
            - url
            - time
        required:
          type: boolean
        placeholder:
          type: string
          nullable: true
        helpText:
          type: string
          nullable: true
        sectionHeading:
          type: string
          nullable: true
          description: Starts a new section above this question.
        options:
          type: array
          items:
            type: string
          description: The choices of a `select` question.
        contactCustomFieldId:
          type: integer
          nullable: true
          description: >-
            A member profile field this question's answer is also saved to,
            linked in the admin portal. Send it back unchanged (or leave it out)
            to keep the link, or null to remove it.
        translations:
          type: object
          nullable: true
          additionalProperties:
            type: object
            additionalProperties:
              type: string
  responses:
    PlatformBadRequest:
      description: Invalid request (bad parameters, or a body that fails validation)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PlatformError'
    PlatformUnauthorized:
      description: Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PlatformError'
    PlatformNotFound:
      description: The requested resource does not exist in this organization
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PlatformError'
    PlatformRateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PlatformError'
  securitySchemes:
    customerApiAuth:
      type: http
      scheme: bearer
      description: >-
        Organization-scoped bearer credential: a customer API key
        (1club_sk_live_...) or an MCP OAuth access token.

````

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