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

# Create a survey

> Creates a block-based survey template from one strict survey document. The endpoint accepts
multilingual authoring maps keyed by real locale codes and converts them to Formbricks'
internal default-language representation. Non-default locale keys in translated content
must be declared in `languages`; undeclared locale keys return `unsupported_locale` in
`invalid_params` instead of silently mutating workspace languages.

`blocks[].id` and `variables[].id` are stable public identifiers. They may be omitted on
create, in which case the server generates cuid2 ids. If the same create request needs to
reference a block or variable from logic, provide explicit valid ids and use those references
consistently.

For normal sequential surveys, omit `logic` and `logicFallback`. `logicFallback` is only valid
when the same block has at least one `logic` rule; otherwise the API returns
`invalid_reference`.

`type` may be omitted or set to `link` or `app`. App surveys additionally accept the `distribution` (display options + triggers) and `targeting` (contact segment filters) objects; both are rejected for link surveys. Trigger ids must reference existing workspace action classes (discover them via `GET /api/v3/action-classes`), and `targeting.filters` references (contact-attribute keys and segments) must exist in the workspace. Unsupported fields are rejected instead of ignored.




## OpenAPI

````yaml /api-v3-reference/openapi.yml post /api/v3/surveys
openapi: 3.1.1
info:
  title: Formbricks API v3
  description: >
    **Surveys**: **GET /api/v3/surveys**, **POST /api/v3/surveys**, **POST
    /api/v3/surveys/generate**, **POST /api/v3/surveys/validate**, **GET
    /api/v3/surveys/{surveyId}**, **PATCH /api/v3/surveys/{surveyId}**, and
    **DELETE /api/v3/surveys/{surveyId}**.


    **Workflows extension**: **GET /api/v3/workflows**, **POST
    /api/v3/workflows**, **GET /api/v3/workflows/{workflowId}**, **PATCH
    /api/v3/workflows/{workflowId}**, **DELETE /api/v3/workflows/{workflowId}**,
    **POST /api/v3/workflows/{workflowId}/duplicate**, **POST
    /api/v3/workflows/{workflowId}/enable**, **POST
    /api/v3/workflows/{workflowId}/disable**, **POST
    /api/v3/workflows/{workflowId}/archive**, **POST
    /api/v3/workflows/{workflowId}/unarchive**, **POST
    /api/v3/workflows/{workflowId}/test**, **GET /api/v3/workflows/runs**, and
    **GET /api/v3/workflows/runs/{runId}**.


    Authenticate all management endpoints with **session cookie** or
    **`x-api-key`** (management key with access to the workspace).


    **Spec location:** source of truth is the multi-file tree at
    `docs/api-v3-reference/src/` (root `openapi.yml` plus one file per path and
    component); `docs/api-v3-reference/openapi.yml` is the generated single-file
    bundle (alongside v2 at `docs/api-v2-reference/openapi.yml`).


    **workspaceId**

    `workspaceId` is the canonical container identifier for this API. It is a
    **required query parameter on collection read endpoints** (`GET
    /api/v3/surveys`, `GET /api/v3/action-classes`, `GET
    /api/v3/contact-attribute-keys`) because those operate within a workspace.
    **`POST /api/v3/surveys` instead requires `workspaceId` in the request
    body**, not as a query parameter. Single-resource endpoints
    (`GET`/`PATCH`/`DELETE /api/v3/surveys/{surveyId}`) locate the resource by
    its globally-unique id and resolve the workspace from it, so they do **not**
    accept a `workspaceId` query parameter — sending one returns **400**
    (`Unrecognized key: "workspaceId"`).


    **Auth**

    Authenticate with either a session cookie or **`x-api-key`**. In dual-auth
    mode, V3 checks the API key first when the header is present, otherwise it
    uses the session path. Unauthenticated callers get **401** before query
    validation.


    **Pagination**

    Cursor-based pagination with **limit** + opaque **cursor** token. Responses
    return `meta.nextCursor`; pass that value back as `cursor` to fetch the next
    page. Survey list responses also include `meta.totalCount`, the total number
    of surveys matching the current filters across all pages. Workflow list
    responses do not require total counts in Scope 1. There is no `offset` in
    this contract.


    **Filtering**

    Filters use explicit operator-style query parameters under the
    **`filter[...]` family**. The survey list supports `filter[name][contains]`,
    `filter[status][in]`, and `filter[type][in]`. The workflow list supports
    `filter[name][contains]` and `filter[status][in]`; the workflow run list
    supports `filter[status][in]` and `filter[isDryRun][eq]`. Resource-scoping
    identifiers (`workspaceId`, `workflowId`, `responseId`) stay plain query
    parameters. Multi-value filters use repeated keys or comma-separated values
    (e.g. `filter[status][in]=draft&filter[status][in]=inProgress` or
    `filter[status][in]=draft,inProgress`). Sorting remains a flat `sortBy`
    query parameter.


    **Security**

    Missing/forbidden workspace returns **403** with a generic message (not
    **404**) so resource existence is not leaked. Workflow and workflow-run
    detail endpoints also return **403** for unknown IDs or inaccessible IDs to
    avoid IDOR leaks. List responses use `private, no-store`.


    **AI survey creation**

    `POST /api/v3/surveys/generate` returns a draft `POST /api/v3/surveys`
    payload plus validation metadata. It does not create a survey. It generates
    survey content for both `link` and `app` surveys; for `app` it seeds a
    default `distribution` (display once, no triggers, no targeting) that you
    finish configuring before publishing.

    Prompt text is sent to the configured AI provider for generation, but it is
    not stored by this endpoint, not logged by default, and not included in
    audit data.


    **Feedback Records** `/api/v3/feedbackRecords` is served by the **API
    gateway** (Envoy on Formbricks Cloud, Traefik in the one-click Docker
    install), which authorizes each call against the dataset and forwards it to
    the feedback store — not by the application routes that serve the rest of
    v3. Three consequences: the payload is the store's own, so members and
    filters are **`snake_case`** (`tenant_id`, `value_text`) rather than v3's
    camelCase; the dataset is named **`tenant_id`** on the wire and is required
    on every collection operation (it is the feedback dataset id, which the MCP
    `list_feedback_datasets` tool reports as `dataset_id`); and errors come in
    two shapes — gateway refusals (**400**, **401**, **403**, **413**, **503**)
    are `text/plain`, while the store's own problems are
    `application/problem+json` with a stable `code`. These eight operations are
    the whole of the gateway-served surface; any other path it receives under
    the prefix answers **400**. Authenticate with **`x-api-key`**: the key needs
    a permission on a workspace the dataset is assigned to — `read` to list, get
    and search, `write` to create and update, `manage` to delete — and may
    update or delete only in a dataset assigned to exactly one workspace.

    **OpenAPI**

    This spec is **not** produced by `pnpm generate-api-specs` (that script only
    builds v2 → `docs/api-v2-reference/openapi.yml`). When the route contract
    changes, edit the files under `docs/api-v3-reference/src/`, then run `pnpm
    api:v3:bundle` to regenerate the committed single-file bundle. `pnpm
    api:v3:lint` lints the source tree; `pnpm api:v3:check` (CI) fails when the
    bundle is stale.


    **Workflows Scope 1**

    Workflows are a follow-up extension of the existing v3 Survey API work. The
    Survey API establishes API-first survey authoring; Workflows build on that
    same v3 style to automate actions around survey responses through
    workspace-scoped JSON definitions. Scope 1 models the existing Follow-ups
    capability as workflows: one `response.completed` trigger with an optional
    `endingCardIds` filter, and `send_email` actions with Follow-up email field
    parity. The definition and run shapes mirror the shared Zod schemas in
    `packages/workflows` (ENG-1100), which are the implementation source of
    truth for this contract.


    Workflows use `draft`, `enabled`, and `disabled` lifecycle states plus an
    `archived` soft-delete state. Status only changes through the lifecycle
    endpoints (`enable`, `disable`, `archive`, `unarchive`); it is not writable
    via `POST` or `PATCH`. Enabling validates that the definition is executable
    and snapshots an immutable workflow version; runs reference that snapshot
    via `workflowVersionId`. `POST .../test` is a dry run: it validates the
    definition and resolves the trigger's references synchronously, creating no
    run and sending no email, and returns `{ workflowId, ok, problems }`
    directly. Real runs execute asynchronously and are polled via `GET
    /api/v3/workflows/runs/{runId}`.


    **Overview migration note**

    The v3-backed survey overview page intentionally removes actions that are
    not yet exposed by this contract: `Created by` filtering, `Duplicate`,
    `Copy...`, `Preview`, and `Copy link`.


    **Next steps (out of scope for this spec)**

    Additional v3 survey update endpoints, optional ETag/304, field selection,
    translation-specific writes, and survey version history.
  version: 0.1.0
  x-implementation-notes:
    route: apps/web/app/api/v3/surveys/route.ts
    generate-route: apps/web/app/api/v3/surveys/generate/route.ts
    query-parser: apps/web/app/api/v3/surveys/parse-v3-surveys-list-query.ts
    auth: apps/web/app/api/v3/lib/auth.ts
    workspace-resolution: apps/web/app/api/v3/lib/workspace-context.ts
    workflow-contract-ticket: ENG-1101
    workflow-shared-schemas: packages/workflows/src/types (feat/workflows-types)
    workflow-prisma-schema: packages/database/schema/workflows.prisma (feat/workflows-prisma-schema)
    openapi-generated: false
    spec-source: docs/api-v3-reference/src (regenerate bundle with `pnpm api:v3:bundle`)
    pagination-model: cursor
    cursor-pagination: supported
servers:
  - url: https://app.formbricks.com
    description: >-
      Formbricks Cloud. Paths in this spec already include the `/api/v3` prefix;
      self-hosted instances substitute their own origin.
security: []
paths:
  /api/v3/surveys:
    post:
      tags:
        - V3 Surveys
      summary: Create a survey
      description: >
        Creates a block-based survey template from one strict survey document.
        The endpoint accepts

        multilingual authoring maps keyed by real locale codes and converts them
        to Formbricks'

        internal default-language representation. Non-default locale keys in
        translated content

        must be declared in `languages`; undeclared locale keys return
        `unsupported_locale` in

        `invalid_params` instead of silently mutating workspace languages.


        `blocks[].id` and `variables[].id` are stable public identifiers. They
        may be omitted on

        create, in which case the server generates cuid2 ids. If the same create
        request needs to

        reference a block or variable from logic, provide explicit valid ids and
        use those references

        consistently.


        For normal sequential surveys, omit `logic` and `logicFallback`.
        `logicFallback` is only valid

        when the same block has at least one `logic` rule; otherwise the API
        returns

        `invalid_reference`.


        `type` may be omitted or set to `link` or `app`. App surveys
        additionally accept the `distribution` (display options + triggers) and
        `targeting` (contact segment filters) objects; both are rejected for
        link surveys. Trigger ids must reference existing workspace action
        classes (discover them via `GET /api/v3/action-classes`), and
        `targeting.filters` references (contact-attribute keys and segments)
        must exist in the workspace. Unsupported fields are rejected instead of
        ignored.
      operationId: createSurveyV3
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSurveyRequest'
            examples:
              productFeedback:
                summary: Create a multilingual block-based survey
                value:
                  workspaceId: clxx1234567890123456789012
                  name: Product Feedback Survey
                  status: draft
                  defaultLanguage: en-US
                  metadata:
                    cx_operation: enterprise_onboarding
                    title:
                      en-US: Product Feedback
                      de-DE: Produktfeedback
                  languages:
                    - code: de-DE
                      enabled: true
                  welcomeCard:
                    enabled: true
                    headline:
                      en-US: Welcome
                      de-DE: Willkommen
                  blocks:
                    - name: Main Block
                      elements:
                        - id: satisfaction
                          type: openText
                          headline:
                            en-US: What should we improve?
                            de-DE: Was sollen wir verbessern?
                          required: true
                  endings: []
                  hiddenFields:
                    enabled: false
                  variables: []
              appSurvey:
                summary: Create an in-app (app) survey with distribution and triggers
                description: >
                  App surveys are shown inside your web or mobile app.
                  `distribution` controls display behavior and the action
                  classes that trigger the survey (by id — discover ids via `GET
                  /api/v3/action-classes`). `targeting.filters` scopes the
                  audience; an empty array targets everyone. `type` is immutable
                  after creation.
                value:
                  workspaceId: clxx1234567890123456789012
                  name: In-App Onboarding Feedback
                  type: app
                  status: draft
                  defaultLanguage: en-US
                  blocks:
                    - name: Onboarding
                      elements:
                        - id: onboarding_feedback
                          type: openText
                          headline:
                            en-US: How was your setup experience?
                          required: false
                  endings: []
                  hiddenFields:
                    enabled: false
                  variables: []
                  distribution:
                    displayOption: respondMultiple
                    recontactDays: 7
                    delay: 5
                    triggers:
                      - actionClassId: clyy1234567890123456789012
                  targeting:
                    filters:
                      - id: clf0aaaaaaaaaaaaaaaaaaaa01
                        connector: null
                        resource:
                          id: clf1aaaaaaaaaaaaaaaaaaaa01
                          root:
                            type: attribute
                            contactAttributeKey: plan
                          qualifier:
                            operator: equals
                          value: pro
                      - id: clf2aaaaaaaaaaaaaaaaaaaa01
                        connector: and
                        resource:
                          - id: clf3aaaaaaaaaaaaaaaaaaaa01
                            connector: null
                            resource:
                              id: clf4aaaaaaaaaaaaaaaaaaaa01
                              root:
                                type: attribute
                                contactAttributeKey: role
                              qualifier:
                                operator: equals
                              value: admin
                          - id: clf5aaaaaaaaaaaaaaaaaaaa01
                            connector: or
                            resource:
                              id: clf6aaaaaaaaaaaaaaaaaaaa01
                              root:
                                type: attribute
                                contactAttributeKey: role
                              qualifier:
                                operator: equals
                              value: owner
              sequentialProductSurvey:
                summary: Create a richer sequential survey without logicFallback
                description: >
                  Multi-block survey with generated block and variable ids.
                  Sequential surveys should

                  omit `logic` and `logicFallback`; blocks are shown in array
                  order.
                value:
                  workspaceId: clxx1234567890123456789012
                  name: Sequential Product Survey
                  defaultLanguage: en-US
                  languages:
                    - code: de-DE
                      enabled: true
                  metadata:
                    cx_operation: product_feedback
                    title:
                      en-US: Sequential Product Survey
                      de-DE: Sequenzielle Produktumfrage
                  welcomeCard:
                    enabled: true
                    headline:
                      en-US: Help us improve the product
                      de-DE: Hilf uns, das Produkt zu verbessern
                  hiddenFields:
                    enabled: true
                    fieldIds:
                      - account_id
                      - plan
                  variables:
                    - name: feedback_score
                      type: number
                      value: 0
                  blocks:
                    - name: Satisfaction
                      elements:
                        - id: overall_rating
                          type: rating
                          headline:
                            en-US: How satisfied are you with the product?
                            de-DE: Wie zufrieden bist du mit dem Produkt?
                          required: true
                          range: 5
                          scale: smiley
                        - id: product_area
                          type: multipleChoiceSingle
                          headline:
                            en-US: Which area should we improve first?
                            de-DE: Welchen Bereich sollten wir zuerst verbessern?
                          required: true
                          choices:
                            - id: onboarding
                              label:
                                en-US: Onboarding
                                de-DE: Onboarding
                            - id: analytics
                              label:
                                en-US: Analytics
                                de-DE: Analysen
                    - name: Feedback
                      elements:
                        - id: free_text_feedback
                          type: openText
                          headline:
                            en-US: What is one thing we should change?
                            de-DE: Was sollten wir ändern?
                          required: false
                          longAnswer: true
                  endings:
                    - id: cmpsequentialend0000000000
                      type: endScreen
                      headline:
                        en-US: Thanks for your feedback!
                        de-DE: Danke für dein Feedback!
      responses:
        '201':
          description: Survey created successfully
          headers:
            Location:
              schema:
                type: string
              description: Relative URL of the created survey resource.
              example: /api/v3/surveys/clsv1234567890123456789012
            X-Request-Id:
              schema:
                type: string
              description: Request correlation ID
            Cache-Control:
              schema:
                type: string
              example: private, no-store
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/SurveyResource'
        '400':
          description: >
            Bad Request — the document failed schema validation, i.e. any rule
            checkable from the request body alone, without consulting stored
            state. Covers: invalid JSON; unknown or unsupported fields
            (including `distribution`/`targeting` sent on a `link` survey, which
            are `app`-only); missing required fields; wrong types or
            out-of-range values; bad enum values; malformed multilingual maps;
            and intra-document field-combination rules — notably `displayLimit`
            is required (must be >= 1) when `displayOption` is `displaySome`.
            Cross-reference failures that need stored state to detect return
            **422** instead. Every offending field is itemized in
            `invalid_params`. Unknown or forbidden fields carry `code:
            unsupported_field` (e.g. `distribution` on a link survey); omissions
            carry `code: missing_required_field` (e.g. `displayLimit` when
            `displayOption` is `displaySome`). Bare type, range, enum, and
            malformed-locale-map violations report the field `name` with a
            human-readable `reason` and no machine `code`.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '401':
          $ref: '#/components/responses/V3Unauthorized'
        '403':
          description: >-
            Forbidden — no write access, missing external URL permission,
            missing Contacts entitlement for app-survey targeting, or workspace
            does not exist (404 not used; avoids existence leak)
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '422':
          description: >-
            Unprocessable Content — the document passed schema validation but
            failed a cross-reference check that requires stored state to detect:
            duplicate stable ids, dangling logic/jump references, undeclared
            locale keys used in content, invalid media URLs, a
            `distribution.triggers[].actionClassId` referencing an action class
            that does not exist in the workspace (discover valid ids via `GET
            /api/v3/action-classes`), or a `targeting.filters` entry that cannot
            resolve in the workspace — an unknown contact-attribute key or
            segment, an unsupported person identifier, or an unknown device
            value. Also returned when the document passes this endpoint's
            request schema but is rejected by the stricter validation the survey
            service applies on write — for example a CTA `buttonUrl` whose
            scheme the service does not accept. The `invalid_params` array
            pinpoints each issue (e.g. `code: invalid_reference` with the
            offending `identifier`).
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '429':
          $ref: '#/components/responses/V3TooManyRequests'
        '500':
          $ref: '#/components/responses/V3InternalServerError'
      security:
        - sessionAuth: []
        - apiKeyAuth: []
components:
  schemas:
    CreateSurveyRequest:
      type: object
      description: >
        Strict v3 survey creation document. This endpoint accepts survey
        structure only: name,

        metadata, languages, welcome card, blocks/elements/logic, endings,
        hidden fields, and variables.

        App surveys (`type: app`) additionally accept the `distribution`
        (display options + triggers) and `targeting` (contact segment filters)
        objects; these are rejected for `link` surveys.

        It rejects legacy `questions` and out-of-scope settings such as styling,
        follow-ups, recaptcha, single-use/email verification, slug, custom
        scripts, analytics fields, timestamps, and `createdBy`.


        Translatable fields use real locale-code maps. The map must include the
        canonical

        `defaultLanguage` key, such as `en-US`, so the server can persist the
        internal default

        translation. Locale keys must be canonical BCP 47 codes such as `de-DE`,
        `pt-PT`, or

        `zh-Hans-CN`. Non-default locale keys must be declared in `languages`;
        undeclared locale

        keys in metadata, welcome cards, blocks, or endings are rejected with
        `unsupported_locale`.


        `blocks[].id` and `variables[].id` may be omitted on create and will be
        generated by the

        server. Provide explicit cuid2 ids when other fields in the same request
        reference them.

        For normal sequential flow, omit `logicFallback`. It is only valid
        together with a non-empty

        `logic` array on the same block.
      required:
        - workspaceId
        - name
        - blocks
      properties:
        workspaceId:
          type: string
          format: cuid2
          description: >-
            Workspace where the survey will be created. Requires read/write
            access.
        name:
          type: string
          minLength: 1
        type:
          type: string
          enum:
            - link
            - app
          default: link
          description: >
            Survey type. `link` surveys are distributed by URL; `app` surveys
            are shown in-app and configure their runtime behavior via the
            `distribution` and `targeting` objects. `type` is immutable after
            creation (it cannot be changed via PATCH).
        status:
          type: string
          enum:
            - draft
            - inProgress
            - paused
            - completed
          default: draft
        metadata:
          allOf:
            - $ref: '#/components/schemas/SurveyMetadata'
          default: {}
        defaultLanguage:
          allOf:
            - $ref: '#/components/schemas/LocaleCode'
          default: en-US
          description: >-
            Default authoring locale. Translatable maps must include this locale
            key.
        languages:
          type: array
          default: []
          items:
            $ref: '#/components/schemas/CreateSurveyLanguage'
          description: >
            Optional survey language configuration. Every non-default locale
            used by translatable maps

            must be declared here; omitted languages are not inferred from map
            keys.
        welcomeCard:
          allOf:
            - $ref: '#/components/schemas/SurveyWelcomeCard'
          default:
            enabled: false
        blocks:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/CreateSurveyBlock'
        endings:
          type: array
          default: []
          items:
            $ref: '#/components/schemas/SurveyEnding'
        hiddenFields:
          allOf:
            - $ref: '#/components/schemas/SurveyHiddenFields'
          default:
            enabled: false
        variables:
          type: array
          default: []
          items:
            $ref: '#/components/schemas/CreateSurveyVariable'
        distribution:
          allOf:
            - $ref: '#/components/schemas/SurveyDistribution'
          description: >-
            App-survey runtime/display settings. Only valid when `type` is
            `app`.
        targeting:
          allOf:
            - $ref: '#/components/schemas/SurveyTargeting'
          description: App-survey contact targeting. Only valid when `type` is `app`.
      additionalProperties: false
    SurveyResource:
      type: object
      required:
        - id
        - workspaceId
        - createdAt
        - updatedAt
        - archivedAt
        - name
        - type
        - status
        - metadata
        - defaultLanguage
        - languages
        - welcomeCard
        - blocks
        - endings
        - hiddenFields
        - variables
      properties:
        id:
          type: string
        workspaceId:
          type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        archivedAt:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            Soft-delete/archive marker (ISO 8601); `null` when the survey is
            active.
        name:
          type: string
        type:
          type: string
          enum:
            - link
            - app
            - website
            - web
        status:
          type: string
          enum:
            - draft
            - inProgress
            - paused
            - completed
        metadata:
          anyOf:
            - $ref: '#/components/schemas/SurveyMetadata'
            - type: 'null'
        defaultLanguage:
          type: string
          description: >-
            Emitted language code/tag for the survey default language. The
            internal `default` translation key is never exposed.
        languages:
          type: array
          items:
            $ref: '#/components/schemas/SurveyLanguage'
        welcomeCard:
          $ref: '#/components/schemas/SurveyWelcomeCard'
        blocks:
          type: array
          items:
            $ref: '#/components/schemas/SurveyBlock'
        endings:
          type: array
          items:
            $ref: '#/components/schemas/SurveyEnding'
        hiddenFields:
          $ref: '#/components/schemas/SurveyHiddenFields'
        variables:
          type: array
          items:
            $ref: '#/components/schemas/SurveyVariable'
        distribution:
          allOf:
            - $ref: '#/components/schemas/SurveyDistribution'
          description: >-
            App-survey runtime/display settings. Present only for app surveys;
            omitted for link surveys.
        targeting:
          allOf:
            - $ref: '#/components/schemas/SurveyTargeting'
          description: >-
            App-survey contact targeting. Present only for app surveys; omitted
            for link surveys.
    Problem:
      type: object
      description: >-
        RFC 9457 Problem Details for HTTP APIs (`application/problem+json`).
        Responses typically include a machine-readable `code` field alongside
        `title`, `status`, `detail`, and `requestId`.
      required:
        - title
        - status
        - detail
        - requestId
      properties:
        type:
          type: string
          format: uri
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        instance:
          type: string
        code:
          type: string
          enum:
            - ai_features_not_enabled
            - ai_generated_payload_invalid
            - ai_instance_not_configured
            - ai_output_too_long
            - ai_smart_tools_disabled
            - bad_gateway
            - bad_request
            - conflict
            - forbidden
            - internal_server_error
            - invalid_workflow_state
            - not_authenticated
            - not_found
            - payload_too_large
            - service_unavailable
            - too_many_requests
            - unprocessable_content
            - workflow_not_executable
        requestId:
          type: string
        details:
          type: object
        invalid_params:
          type: array
          items:
            $ref: '#/components/schemas/InvalidParam'
    SurveyMetadata:
      type: object
      description: >
        Arbitrary JSON survey context for customer- or operation-specific
        metadata. v3 preserves

        arbitrary metadata values as-is. If present, `title` and `description`
        are treated as

        translatable text maps and returned with real locale-code keys.
      properties:
        title:
          $ref: '#/components/schemas/TranslatableText'
        description:
          $ref: '#/components/schemas/TranslatableText'
      additionalProperties: true
    LocaleCode:
      type: string
      pattern: ^[a-z]{2}(?:-[A-Z][a-z]{3})?-[A-Z]{2}$
      description: >-
        Canonical locale code accepted by v3 survey APIs, for example `en-US`,
        `de-DE`, or `zh-Hans-CN`.
      example: en-US
    CreateSurveyLanguage:
      type: object
      description: >-
        Language to connect to the created survey. `default`, if provided, must
        match `defaultLanguage`.
      required:
        - code
      properties:
        code:
          $ref: '#/components/schemas/LocaleCode'
        default:
          type: boolean
          description: >-
            Optional marker for readability; only the `defaultLanguage` entry
            may set this to true.
        enabled:
          type: boolean
          default: true
          description: Whether this language is enabled for respondent-facing delivery.
      additionalProperties: false
    SurveyWelcomeCard:
      type: object
      description: Optional card shown before the first survey block.
      required:
        - enabled
      properties:
        enabled:
          type: boolean
        headline:
          $ref: '#/components/schemas/TranslatableText'
        subheader:
          $ref: '#/components/schemas/TranslatableText'
        buttonLabel:
          $ref: '#/components/schemas/TranslatableText'
        fileUrl:
          type: string
        videoUrl:
          type: string
        timeToFinish:
          type: boolean
        showResponseCount:
          type: boolean
      additionalProperties: false
    CreateSurveyBlock:
      type: object
      description: >
        Block accepted by `POST /api/v3/surveys`. `id` may be omitted and will
        be generated by the

        server. Provide an explicit cuid2 id when logic in the same request
        needs to jump to this block.

        For normal sequential flow, omit `logic` and `logicFallback`.
        `logicFallback` is only valid

        when this same block has at least one `logic` rule.
      required:
        - name
        - elements
      properties:
        id:
          type: string
          format: cuid2
          description: Optional stable block id. Generated when omitted.
        name:
          type: string
          minLength: 1
        elements:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/SurveyElement'
        logic:
          type: array
          items:
            $ref: '#/components/schemas/SurveyBlockLogic'
        logicFallback:
          type: string
          format: cuid2
          description: >
            Block or ending id used when no logic condition matches. Only valid
            when this same block

            has at least one `logic` rule; omit it for normal sequential flow.
        buttonLabel:
          $ref: '#/components/schemas/TranslatableText'
        backButtonLabel:
          $ref: '#/components/schemas/TranslatableText'
      additionalProperties: false
    SurveyEnding:
      oneOf:
        - $ref: '#/components/schemas/SurveyEndScreenEnding'
        - $ref: '#/components/schemas/SurveyRedirectEnding'
      discriminator:
        propertyName: type
        mapping:
          endScreen:
            $ref: '#/components/schemas/SurveyEndScreenEnding'
          redirectToUrl:
            $ref: '#/components/schemas/SurveyRedirectEnding'
      description: >
        Ending reached after the last block or a jump action. `type` selects the
        allowed shape;

        unsupported fields are rejected instead of ignored.
    SurveyHiddenFields:
      type: object
      description: >
        Hidden fields, sometimes called embedded data in other survey products.
        Field ids are stable public identifiers and may be referenced by logic,
        recall, quotas, integrations, and response data.


        **Naming rule for new field ids** — the same rule the survey editor
        applies (ENG-2539): a new id must start with a lowercase letter and then
        contain only lowercase letters, numbers and underscores
        (`^[a-z][a-z0-9_]*$`), and must not take a reserved name. Reserved are
        the link-survey system params and internal ids — `userId`, `source`,
        `suid`, `end`, `start`, `welcomeCard`, `hidden`, `verifiedEmail`,
        `multiLanguage`, `embed`, `verify`, `suToken`, `lang`, `preview`,
        `startAt`, `skipPrefilled`, `offlineSupport` (matched
        case-insensitively) — and the names of auto-captured system fields,
        which every survey can already read without declaring them: `source`,
        `url`, `country`, `action`, `browser`, `os`, `deviceType`, `ipAddress`,
        `finished`, `language`, `locale`, `responseId`, `surveyId`,
        `durationSeconds`, `startedAt`, `finishedAt`, `pagePath`,
        `pageReferrer`, `utmSource`, `utmMedium`, `utmCampaign`, `utmTerm`,
        `utmContent`, `screenWidth`, `screenHeight`, `viewportWidth`,
        `viewportHeight`, `timezone`.


        **Ids a survey already declares are grandfathered**: they always load,
        and re-sending the survey unchanged never fails validation — only
        *adding* such a name as a new field is refused. Deleting a grandfathered
        field spends the reprieve: once it is gone from the saved survey, the
        name can no longer be re-added. Note this means re-creating a survey
        from an exported document can fail with a 400 when the export declares a
        name the rule refuses; rename the field in the payload before importing.
        The `pattern` below describes what stored surveys may hold (the
        pre-existing lenient charset), not what a new id may look like.
      required:
        - enabled
      properties:
        enabled:
          type: boolean
        fieldIds:
          type: array
          items:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
          uniqueItems: true
      additionalProperties: false
    CreateSurveyVariable:
      oneOf:
        - $ref: '#/components/schemas/CreateSurveyNumberVariable'
        - $ref: '#/components/schemas/CreateSurveyTextVariable'
      description: >
        Survey variable accepted by `POST /api/v3/surveys`. `id` may be omitted
        and will be

        generated by the server. Provide an explicit cuid2 id when logic in the
        same request needs

        to reference this variable.
    SurveyDistribution:
      type: object
      description: >
        App-survey runtime/display settings. Only valid for `type: app`;
        rejected for link surveys.

        **Replacement semantics (destructive):** when `distribution` is provided
        on PATCH it fully replaces the stored object — any sub-field you omit is
        reset to its default, NOT preserved. In particular, omitting `triggers`
        removes ALL existing triggers, and omitting `displayOption` resets it to
        `displayOnce`. To change one setting, send the complete desired
        distribution. To leave distribution untouched, omit the whole
        `distribution` key.
      properties:
        displayOption:
          type: string
          enum:
            - displayOnce
            - displayMultiple
            - respondMultiple
            - displaySome
          default: displayOnce
          description: >
            How often the survey may be shown to a contact. `displaySome` shows
            it up to `displayLimit` times (or until the contact responds,
            whichever comes first).
        displayPercentage:
          type:
            - number
            - 'null'
          minimum: 0.01
          maximum: 100
          default: null
          description: >
            Independent throttle: show the survey to only this percentage of
            triggered contacts. Optional and valid with any `displayOption`;
            null/omitted means no throttle (shown to everyone).
        displayLimit:
          type:
            - integer
            - 'null'
          minimum: 0
          default: null
          description: >
            Maximum number of times the survey is shown to a single contact.
            Required (must be >= 1) when `displayOption` is `displaySome`;
            otherwise optional.
        recontactDays:
          type:
            - integer
            - 'null'
          minimum: 0
          default: null
          description: >-
            Days to wait before showing this survey again to the same contact.
            Null uses the workspace default.
        autoClose:
          type:
            - integer
            - 'null'
          minimum: 0
          default: null
          description: >-
            Seconds of inactivity after which the survey widget auto-closes.
            Null disables auto-close.
        autoComplete:
          type:
            - integer
            - 'null'
          minimum: 1
          default: null
          description: >-
            Automatically stop collecting responses after this many completed
            responses. Null disables it.
        delay:
          type: integer
          minimum: 0
          default: 0
          description: Seconds to wait after a trigger fires before showing the survey.
        triggers:
          type: array
          default: []
          items:
            $ref: '#/components/schemas/SurveyTrigger'
          description: >
            Action classes that trigger the survey. Every id must reference an
            existing workspace action class. On PATCH this list fully replaces
            the survey's triggers — omitting it (or sending `[]`) removes all
            existing triggers.
      additionalProperties: false
      example:
        displayOption: displaySome
        displayLimit: 3
        displayPercentage: null
        recontactDays: 7
        autoClose: null
        autoComplete: null
        delay: 0
        triggers:
          - actionClassId: clyy1234567890123456789012
    SurveyTargeting:
      type: object
      description: >
        App-survey contact targeting. Only valid for `type: app`. `filters: []`
        targets everyone. Setting or changing non-empty filters requires the
        Contacts entitlement; otherwise create/patch returns 403.


        `attribute` filters reference workspace contact-attribute keys (discover
        them via `GET /api/v3/contact-attribute-keys`) and `segment` filters
        reference other segment ids — both are workspace-scoped resources you
        must obtain before constructing filters (an invalid key/id targets no
        one rather than erroring). See `SegmentFilter` for the per-`root.type`
        operator sets.
      required:
        - filters
      properties:
        filters:
          $ref: '#/components/schemas/SegmentFilters'
      additionalProperties: false
    SurveyLanguage:
      type: object
      description: >
        Language configured for this survey. GET responses expose the
        server-emitted code/tag used as the

        translatable map key. Existing surveys can use region-qualified,
        language-only, or script-only codes.

        Disabled languages can still be read by the management API so unfinished
        translations can be completed.
      required:
        - code
        - default
        - enabled
      properties:
        code:
          type: string
          description: >-
            Server-emitted survey language code/tag used as the translatable map
            key.
          example: en-US
        alias:
          type:
            - string
            - 'null'
          description: >-
            Optional configured alias accepted by `?lang` for compatibility and
            agent discovery.
          example: english
        default:
          type: boolean
          description: Whether this is the default authoring language.
        enabled:
          type: boolean
          description: Whether this language is enabled for respondent-facing delivery.
    SurveyBlock:
      type: object
      description: >
        Block-based survey section. Block ids are stable public identifiers.
        Logic and fallbacks can

        jump to block ids or ending ids, so clients and agents should preserve
        ids unless intentionally

        creating/deleting a block.
      required:
        - id
        - name
        - elements
      properties:
        id:
          type: string
          format: cuid2
          description: Stable block id.
        name:
          type: string
          minLength: 1
        elements:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/SurveyElement'
        logic:
          type: array
          items:
            $ref: '#/components/schemas/SurveyBlockLogic'
        logicFallback:
          type: string
          format: cuid2
          description: >
            Block or ending id used when no logic condition matches. Only valid
            when this same block

            has at least one `logic` rule; omit it for normal sequential flow.
        buttonLabel:
          $ref: '#/components/schemas/TranslatableText'
        backButtonLabel:
          $ref: '#/components/schemas/TranslatableText'
      additionalProperties: false
    SurveyVariable:
      oneOf:
        - $ref: '#/components/schemas/SurveyNumberVariable'
        - $ref: '#/components/schemas/SurveyTextVariable'
      description: >
        Survey variable. Variable ids are stable references used by logic and
        calculation actions.

        Variable names are human-readable labels and must be unique within the
        survey.
    InvalidParam:
      type: object
      description: >
        Field-level validation error. `name` and `reason` are always intended
        for humans.

        Optional machine-readable fields are included when the API can identify
        the semantic error,

        especially for survey locale, identifier, and reference validation used
        by MCP and agent clients.
      required:
        - name
        - reason
      properties:
        name:
          type: string
          description: Dot-path to the invalid request field.
        reason:
          type: string
          description: Human-readable repair guidance.
        code:
          type: string
          enum:
            - dangling_reference
            - duplicate_identifier
            - duplicate_locale
            - forbidden_identifier
            - immutable_identifier
            - invalid_graph
            - invalid_locale
            - invalid_reference
            - missing_required_field
            - missing_translation
            - unsupported_field
            - unsupported_locale
          description: >
            Machine-readable validation category, when available.
            `invalid_graph` covers workflow

            graph-structure violations such as a duplicated trigger edge, an
            unreachable node, or a

            cycle.
        identifier:
          type: string
          description: >-
            Identifier involved in the validation failure, when available. For
            language errors, this can be the normalized language code.
        referenceType:
          type: string
          enum:
            - block
            - element
            - ending
            - hiddenField
            - language
            - variable
            - variableName
            - recall
            - node
            - edge
            - survey
            - endingCard
          description: >-
            Identifier namespace involved in the validation failure. `node`,
            `edge`, `survey`, and `endingCard` are used by workflow validation.
        missingId:
          type: string
          description: Referenced id that does not exist in the submitted survey document.
        firstUsedAt:
          type: string
          description: Dot-path where a duplicate identifier was first used.
        conflictsWith:
          type: string
          description: Dot-path to the existing identifier that conflicts with this field.
      additionalProperties: false
    TranslatableText:
      allOf:
        - $ref: '#/components/schemas/TranslatableTextMap'
      description: >
        Survey authoring text. `GET /api/v3/surveys/{surveyId}` always returns
        maps keyed by the emitted

        `languages[].code` values for this survey. Use `?lang=` to filter which
        language keys are included.

        The internal storage key `default` is never exposed by v3.
      examples:
        - en-US: What should we improve?
          de-DE: Was sollten wir verbessern?
    SurveyElement:
      oneOf:
        - $ref: '#/components/schemas/SurveyOpenTextElement'
        - $ref: '#/components/schemas/SurveyConsentElement'
        - $ref: '#/components/schemas/SurveyMultipleChoiceSingleElement'
        - $ref: '#/components/schemas/SurveyMultipleChoiceMultiElement'
        - $ref: '#/components/schemas/SurveyNpsElement'
        - $ref: '#/components/schemas/SurveyCtaElement'
        - $ref: '#/components/schemas/SurveyRatingElement'
        - $ref: '#/components/schemas/SurveyPictureSelectionElement'
        - $ref: '#/components/schemas/SurveyDateElement'
        - $ref: '#/components/schemas/SurveyFileUploadElement'
        - $ref: '#/components/schemas/SurveyCalElement'
        - $ref: '#/components/schemas/SurveyMatrixElement'
        - $ref: '#/components/schemas/SurveyAddressElement'
        - $ref: '#/components/schemas/SurveyRankingElement'
        - $ref: '#/components/schemas/SurveyContactInfoElement'
        - $ref: '#/components/schemas/SurveyCsatElement'
        - $ref: '#/components/schemas/SurveyCesElement'
      discriminator:
        propertyName: type
        mapping:
          openText:
            $ref: '#/components/schemas/SurveyOpenTextElement'
          consent:
            $ref: '#/components/schemas/SurveyConsentElement'
          multipleChoiceSingle:
            $ref: '#/components/schemas/SurveyMultipleChoiceSingleElement'
          multipleChoiceMulti:
            $ref: '#/components/schemas/SurveyMultipleChoiceMultiElement'
          nps:
            $ref: '#/components/schemas/SurveyNpsElement'
          cta:
            $ref: '#/components/schemas/SurveyCtaElement'
          rating:
            $ref: '#/components/schemas/SurveyRatingElement'
          pictureSelection:
            $ref: '#/components/schemas/SurveyPictureSelectionElement'
          date:
            $ref: '#/components/schemas/SurveyDateElement'
          fileUpload:
            $ref: '#/components/schemas/SurveyFileUploadElement'
          cal:
            $ref: '#/components/schemas/SurveyCalElement'
          matrix:
            $ref: '#/components/schemas/SurveyMatrixElement'
          address:
            $ref: '#/components/schemas/SurveyAddressElement'
          ranking:
            $ref: '#/components/schemas/SurveyRankingElement'
          contactInfo:
            $ref: '#/components/schemas/SurveyContactInfoElement'
          csat:
            $ref: '#/components/schemas/SurveyCsatElement'
          ces:
            $ref: '#/components/schemas/SurveyCesElement'
      description: >
        Survey element/question inside a block. Element ids are stable public
        identifiers used by

        logic, recall strings, response data, quotas, integrations, and
        analysis. `type` selects the

        allowed shape; unsupported fields are rejected instead of ignored.
    SurveyBlockLogic:
      type: object
      description: Conditional logic rule evaluated at block level.
      required:
        - id
        - conditions
        - actions
      properties:
        id:
          type: string
          format: cuid2
        conditions:
          $ref: '#/components/schemas/SurveyConditionGroup'
        actions:
          type: array
          items:
            $ref: '#/components/schemas/SurveyLogicAction'
      additionalProperties: false
    SurveyEndScreenEnding:
      type: object
      description: Visual end screen displayed after survey completion.
      required:
        - id
        - type
      properties:
        id:
          type: string
          format: cuid2
          description: Stable ending id. `jumpToBlock.target` may point to this id.
        type:
          type: string
          enum:
            - endScreen
        headline:
          $ref: '#/components/schemas/TranslatableText'
        subheader:
          $ref: '#/components/schemas/TranslatableText'
        buttonLabel:
          $ref: '#/components/schemas/TranslatableText'
        buttonLink:
          type: string
        imageUrl:
          type: string
        videoUrl:
          type: string
        hideDefaultIcon:
          type: boolean
          description: >-
            Hides the default checkmark icon rendered above the headline when
            the ending carries no image or video. Omit to keep the icon.
      additionalProperties: false
    SurveyRedirectEnding:
      type: object
      description: >
        Redirects the respondent to a URL after survey completion. External
        redirects require the

        organization's external URL permission; otherwise write endpoints return
        `403 Forbidden`.
      required:
        - id
        - type
        - url
      properties:
        id:
          type: string
          format: cuid2
          description: Stable ending id. `jumpToBlock.target` may point to this id.
        type:
          type: string
          enum:
            - redirectToUrl
        url:
          type: string
          format: uri
          description: >-
            External redirect URL. Requires the organization's external URL
            permission.
        label:
          type: string
          description: Optional internal label for redirect endings.
      additionalProperties: false
    CreateSurveyNumberVariable:
      type: object
      required:
        - name
        - type
        - value
      properties:
        id:
          type: string
          format: cuid2
          description: Optional stable variable id. Generated when omitted.
        name:
          type: string
          pattern: ^[a-z][a-z0-9_]*$
          description: >
            Unique variable name. Must start with a lowercase letter, then
            lowercase letters, numbers and underscores only — the same rule as
            new hidden-field ids (this schema is create-only, so no
            grandfathered name validates against it). Reserved names are
            refused: `userId`, `source`, `suid`, `end`, `start`, `welcomeCard`,
            `hidden`, `verifiedEmail`, `multiLanguage`, `embed`, `verify`,
            `suToken`, `lang`, `preview`, `startAt`, `skipPrefilled`,
            `offlineSupport`, and the auto-captured system field names `source`,
            `url`, `country`, `action`, `browser`, `os`, `deviceType`,
            `ipAddress`, `finished`, `language`, `locale`, `responseId`,
            `surveyId`, `durationSeconds`, `startedAt`, `finishedAt`,
            `pagePath`, `pageReferrer`, `utmSource`, `utmMedium`, `utmCampaign`,
            `utmTerm`, `utmContent`, `screenWidth`, `screenHeight`,
            `viewportWidth`, `viewportHeight`, `timezone`.
        type:
          type: string
          enum:
            - number
        value:
          type: number
          description: Default numeric value.
      additionalProperties: false
    CreateSurveyTextVariable:
      type: object
      required:
        - name
        - type
        - value
      properties:
        id:
          type: string
          format: cuid2
          description: Optional stable variable id. Generated when omitted.
        name:
          type: string
          pattern: ^[a-z][a-z0-9_]*$
          description: >
            Unique variable name. Must start with a lowercase letter, then
            lowercase letters, numbers and underscores only — the same rule as
            new hidden-field ids (this schema is create-only, so no
            grandfathered name validates against it). Reserved names are
            refused: `userId`, `source`, `suid`, `end`, `start`, `welcomeCard`,
            `hidden`, `verifiedEmail`, `multiLanguage`, `embed`, `verify`,
            `suToken`, `lang`, `preview`, `startAt`, `skipPrefilled`,
            `offlineSupport`, and the auto-captured system field names `source`,
            `url`, `country`, `action`, `browser`, `os`, `deviceType`,
            `ipAddress`, `finished`, `language`, `locale`, `responseId`,
            `surveyId`, `durationSeconds`, `startedAt`, `finishedAt`,
            `pagePath`, `pageReferrer`, `utmSource`, `utmMedium`, `utmCampaign`,
            `utmTerm`, `utmContent`, `screenWidth`, `screenHeight`,
            `viewportWidth`, `viewportHeight`, `timezone`.
        type:
          type: string
          enum:
            - text
        value:
          type: string
          description: Default text value.
      additionalProperties: false
    SurveyTrigger:
      type: object
      description: >
        Reference to an existing workspace action class that triggers this app
        survey. Discover available action-class ids with `GET
        /api/v3/action-classes`.
      required:
        - actionClassId
      properties:
        actionClassId:
          type: string
          format: cuid2
          description: Id of an action class in the same workspace.
      additionalProperties: false
    SegmentFilters:
      type: array
      description: >
        Contact targeting filter tree. An empty array targets everyone. Each
        node joins the previous one via its `connector` (`and`/`or`; the first
        node's connector is null). A node's `resource` is either a single filter
        condition or a nested filter group (enabling parenthesized `and`/`or`
        logic).
      items:
        type: object
        required:
          - id
          - connector
          - resource
        properties:
          id:
            type: string
            format: cuid2
          connector:
            type:
              - string
              - 'null'
            enum:
              - and
              - or
              - null
            description: >-
              Logical connector joining this node to the previous one. Null for
              the first node.
          resource:
            oneOf:
              - $ref: '#/components/schemas/SegmentFilter'
              - $ref: '#/components/schemas/SegmentFilters'
            description: >-
              A single filter condition, or a nested filter group for grouped
              logic.
        additionalProperties: false
    SurveyNumberVariable:
      type: object
      description: >
        Number variable. Used by `calculate` logic actions with numeric
        operators such as `add`,

        `subtract`, `multiply`, `divide`, or `assign`.
      required:
        - id
        - name
        - type
        - value
      properties:
        id:
          type: string
          format: cuid2
          description: Stable variable id referenced from logic.
        name:
          type: string
          pattern: ^[a-z0-9_]+$
          description: >-
            Unique variable name. Lowercase letters, numbers, and underscores
            only.
        type:
          type: string
          enum:
            - number
        value:
          type: number
          description: Default numeric value.
      additionalProperties: false
    SurveyTextVariable:
      type: object
      description: >
        Text variable. Used by `calculate` logic actions with text operators
        such as `assign` or `concat`.
      required:
        - id
        - name
        - type
        - value
      properties:
        id:
          type: string
          format: cuid2
          description: Stable variable id referenced from logic.
        name:
          type: string
          pattern: ^[a-z0-9_]+$
          description: >-
            Unique variable name. Lowercase letters, numbers, and underscores
            only.
        type:
          type: string
          enum:
            - text
        value:
          type: string
          description: Default text value.
      additionalProperties: false
    TranslatableTextMap:
      type: object
      description: >-
        Multilingual text map keyed by the emitted `languages[].code` values for
        this survey.
      propertyNames:
        type: string
        description: >-
          Survey language code/tag, for example `en-US`, `de-DE`, or
          `zh-Hans-CN`.
      additionalProperties:
        type: string
    SurveyOpenTextElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          properties:
            type:
              type: string
              enum:
                - openText
            placeholder:
              $ref: '#/components/schemas/TranslatableText'
            longAnswer:
              type: boolean
            inputType:
              type: string
              enum:
                - text
                - email
                - url
                - number
                - phone
              default: text
            insightsEnabled:
              type: boolean
              default: false
            charLimit:
              $ref: '#/components/schemas/SurveyCharLimit'
            validation:
              $ref: '#/components/schemas/SurveyValidation'
      unevaluatedProperties: false
    SurveyConsentElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - label
          properties:
            type:
              type: string
              enum:
                - consent
            label:
              $ref: '#/components/schemas/TranslatableText'
            validation:
              $ref: '#/components/schemas/SurveyValidation'
      unevaluatedProperties: false
    SurveyMultipleChoiceSingleElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - choices
          properties:
            type:
              type: string
              enum:
                - multipleChoiceSingle
            choices:
              type: array
              minItems: 2
              items:
                $ref: '#/components/schemas/SurveyChoice'
            shuffleOption:
              $ref: '#/components/schemas/SurveyShuffleOption'
            otherOptionPlaceholder:
              $ref: '#/components/schemas/TranslatableText'
            displayType:
              $ref: '#/components/schemas/SurveyMultipleChoiceDisplayType'
      unevaluatedProperties: false
    SurveyMultipleChoiceMultiElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - choices
          properties:
            type:
              type: string
              enum:
                - multipleChoiceMulti
            choices:
              type: array
              minItems: 2
              items:
                $ref: '#/components/schemas/SurveyChoice'
            shuffleOption:
              $ref: '#/components/schemas/SurveyShuffleOption'
            otherOptionPlaceholder:
              $ref: '#/components/schemas/TranslatableText'
            validation:
              $ref: '#/components/schemas/SurveyValidation'
            displayType:
              $ref: '#/components/schemas/SurveyMultipleChoiceDisplayType'
      unevaluatedProperties: false
    SurveyNpsElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          properties:
            type:
              type: string
              enum:
                - nps
            lowerLabel:
              $ref: '#/components/schemas/TranslatableText'
            upperLabel:
              $ref: '#/components/schemas/TranslatableText'
            isColorCodingEnabled:
              type: boolean
              default: false
      unevaluatedProperties: false
    SurveyCtaElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          description: >-
            If `buttonExternal` is true, `buttonUrl` and `ctaButtonLabel` are
            required.
          properties:
            type:
              type: string
              enum:
                - cta
            buttonExternal:
              type: boolean
              default: false
            buttonUrl:
              type: string
            ctaButtonLabel:
              $ref: '#/components/schemas/TranslatableText'
        - if:
            required:
              - buttonExternal
            properties:
              buttonExternal:
                const: true
          then:
            required:
              - buttonUrl
              - ctaButtonLabel
      unevaluatedProperties: false
    SurveyRatingElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - scale
            - range
          properties:
            type:
              type: string
              enum:
                - rating
            scale:
              type: string
              enum:
                - number
                - smiley
                - star
            range:
              type: integer
              enum:
                - 3
                - 4
                - 5
                - 6
                - 7
                - 10
            lowerLabel:
              $ref: '#/components/schemas/TranslatableText'
            upperLabel:
              $ref: '#/components/schemas/TranslatableText'
            isColorCodingEnabled:
              type: boolean
              default: false
      unevaluatedProperties: false
    SurveyPictureSelectionElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - choices
          properties:
            type:
              type: string
              enum:
                - pictureSelection
            allowMulti:
              type: boolean
              default: false
            choices:
              type: array
              minItems: 2
              items:
                $ref: '#/components/schemas/SurveyPictureChoice'
            validation:
              $ref: '#/components/schemas/SurveyValidation'
      unevaluatedProperties: false
    SurveyDateElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - format
          properties:
            type:
              type: string
              enum:
                - date
            html:
              $ref: '#/components/schemas/TranslatableText'
            format:
              type: string
              enum:
                - M-d-y
                - d-M-y
                - y-M-d
            validation:
              $ref: '#/components/schemas/SurveyValidation'
      unevaluatedProperties: false
    SurveyFileUploadElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - allowMultipleFiles
          properties:
            type:
              type: string
              enum:
                - fileUpload
            allowMultipleFiles:
              type: boolean
            maxSizeInMB:
              type: number
            allowedFileExtensions:
              type: array
              items:
                type: string
            validation:
              $ref: '#/components/schemas/SurveyValidation'
      unevaluatedProperties: false
    SurveyCalElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - calUserName
          properties:
            type:
              type: string
              enum:
                - cal
            calUserName:
              type: string
              minLength: 1
            calHost:
              type: string
      unevaluatedProperties: false
    SurveyMatrixElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - rows
            - columns
          properties:
            type:
              type: string
              enum:
                - matrix
            rows:
              type: array
              items:
                $ref: '#/components/schemas/SurveyChoice'
            columns:
              type: array
              items:
                $ref: '#/components/schemas/SurveyChoice'
            shuffleOption:
              $ref: '#/components/schemas/SurveyShuffleOption'
            validation:
              $ref: '#/components/schemas/SurveyValidation'
      unevaluatedProperties: false
    SurveyAddressElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - addressLine1
            - addressLine2
            - city
            - state
            - zip
            - country
          properties:
            type:
              type: string
              enum:
                - address
            addressLine1:
              $ref: '#/components/schemas/SurveyToggleInputConfig'
            addressLine2:
              $ref: '#/components/schemas/SurveyToggleInputConfig'
            city:
              $ref: '#/components/schemas/SurveyToggleInputConfig'
            state:
              $ref: '#/components/schemas/SurveyToggleInputConfig'
            zip:
              $ref: '#/components/schemas/SurveyToggleInputConfig'
            country:
              $ref: '#/components/schemas/SurveyToggleInputConfig'
            validation:
              $ref: '#/components/schemas/SurveyValidation'
      unevaluatedProperties: false
    SurveyRankingElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - choices
          properties:
            type:
              type: string
              enum:
                - ranking
            choices:
              type: array
              minItems: 2
              maxItems: 25
              items:
                $ref: '#/components/schemas/SurveyChoice'
            otherOptionPlaceholder:
              $ref: '#/components/schemas/TranslatableText'
            shuffleOption:
              $ref: '#/components/schemas/SurveyShuffleOption'
            validation:
              $ref: '#/components/schemas/SurveyValidation'
      unevaluatedProperties: false
    SurveyContactInfoElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - firstName
            - lastName
            - email
            - phone
            - company
          properties:
            type:
              type: string
              enum:
                - contactInfo
            firstName:
              $ref: '#/components/schemas/SurveyToggleInputConfig'
            lastName:
              $ref: '#/components/schemas/SurveyToggleInputConfig'
            email:
              $ref: '#/components/schemas/SurveyToggleInputConfig'
            phone:
              $ref: '#/components/schemas/SurveyToggleInputConfig'
            company:
              $ref: '#/components/schemas/SurveyToggleInputConfig'
            validation:
              $ref: '#/components/schemas/SurveyValidation'
      unevaluatedProperties: false
    SurveyCsatElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - scale
            - range
          properties:
            type:
              type: string
              enum:
                - csat
            scale:
              type: string
              enum:
                - number
                - smiley
                - star
            range:
              type: integer
              enum:
                - 5
            lowerLabel:
              $ref: '#/components/schemas/TranslatableText'
            upperLabel:
              $ref: '#/components/schemas/TranslatableText'
            isColorCodingEnabled:
              type: boolean
              default: false
      unevaluatedProperties: false
    SurveyCesElement:
      allOf:
        - $ref: '#/components/schemas/SurveyElementBase'
        - type: object
          required:
            - scale
            - range
          properties:
            type:
              type: string
              enum:
                - ces
            scale:
              type: string
              enum:
                - number
                - smiley
                - star
            range:
              type: integer
              enum:
                - 5
                - 7
            lowerLabel:
              $ref: '#/components/schemas/TranslatableText'
            upperLabel:
              $ref: '#/components/schemas/TranslatableText'
            isColorCodingEnabled:
              type: boolean
              default: false
      unevaluatedProperties: false
    SurveyConditionGroup:
      type: object
      required:
        - id
        - connector
        - conditions
      properties:
        id:
          type: string
          format: cuid2
        connector:
          type: string
          enum:
            - and
            - or
        conditions:
          type: array
          items:
            oneOf:
              - $ref: '#/components/schemas/SurveyCondition'
              - $ref: '#/components/schemas/SurveyConditionGroup'
      additionalProperties: false
    SurveyLogicAction:
      oneOf:
        - $ref: '#/components/schemas/SurveyCalculateAction'
        - $ref: '#/components/schemas/SurveyRequireAnswerAction'
        - $ref: '#/components/schemas/SurveyJumpToBlockAction'
      description: >
        Logic action. Keep referenced ids stable: `calculate.variableId` points
        to a variable id,

        `requireAnswer.target` points to an element id, and `jumpToBlock.target`
        points to a block id

        or ending id.
    SegmentFilter:
      description: >
        A single targeting condition. `root.type` selects the subject and
        determines the valid `qualifier.operator` set: `attribute` (a workspace
        contact-attribute key), `person` (a built-in person identifier such as
        `userId`/`email`), `segment` (membership in another segment), or
        `device`. Each variant is one member of this union; the matching member
        is chosen by `root.type`.
      oneOf:
        - title: AttributeFilter
          description: Matches on a workspace contact-attribute value.
          type: object
          required:
            - id
            - root
            - qualifier
            - value
          properties:
            id:
              type: string
              format: cuid2
            root:
              type: object
              required:
                - type
                - contactAttributeKey
              properties:
                type:
                  type: string
                  enum:
                    - attribute
                contactAttributeKey:
                  type: string
                  description: >-
                    A contact-attribute key defined in the workspace (e.g.
                    `plan`, `role`).
              additionalProperties: false
            qualifier:
              type: object
              required:
                - operator
              properties:
                operator:
                  type: string
                  enum:
                    - lessThan
                    - lessEqual
                    - greaterThan
                    - greaterEqual
                    - equals
                    - notEquals
                    - isSet
                    - isNotSet
                    - contains
                    - doesNotContain
                    - startsWith
                    - endsWith
                    - isOlderThan
                    - isNewerThan
                    - isBefore
                    - isAfter
                    - isBetween
                    - isSameDay
              additionalProperties: false
            value:
              $ref: '#/components/schemas/SegmentFilterValue'
          additionalProperties: false
        - title: PersonFilter
          description: Matches on a built-in person identifier.
          type: object
          required:
            - id
            - root
            - qualifier
            - value
          properties:
            id:
              type: string
              format: cuid2
            root:
              type: object
              required:
                - type
                - personIdentifier
              properties:
                type:
                  type: string
                  enum:
                    - person
                personIdentifier:
                  type: string
                  description: A built-in person identifier (e.g. `userId`).
              additionalProperties: false
            qualifier:
              type: object
              required:
                - operator
              properties:
                operator:
                  type: string
                  enum:
                    - equals
                    - notEquals
                    - isSet
                    - isNotSet
                    - contains
                    - doesNotContain
                    - startsWith
                    - endsWith
              additionalProperties: false
            value:
              $ref: '#/components/schemas/SegmentFilterValue'
          additionalProperties: false
        - title: SegmentFilter
          description: Matches on membership in another segment.
          type: object
          required:
            - id
            - root
            - qualifier
            - value
          properties:
            id:
              type: string
              format: cuid2
            root:
              type: object
              required:
                - type
                - segmentId
              properties:
                type:
                  type: string
                  enum:
                    - segment
                segmentId:
                  type: string
                  description: Id of the segment to test membership against.
              additionalProperties: false
            qualifier:
              type: object
              required:
                - operator
              properties:
                operator:
                  type: string
                  enum:
                    - userIsIn
                    - userIsNotIn
              additionalProperties: false
            value:
              $ref: '#/components/schemas/SegmentFilterValue'
          additionalProperties: false
        - title: DeviceFilter
          description: Matches on the contact's device type.
          type: object
          required:
            - id
            - root
            - qualifier
            - value
          properties:
            id:
              type: string
              format: cuid2
            root:
              type: object
              required:
                - type
                - deviceType
              properties:
                type:
                  type: string
                  enum:
                    - device
                deviceType:
                  type: string
                  description: Device type (e.g. `desktop`, `phone`).
              additionalProperties: false
            qualifier:
              type: object
              required:
                - operator
              properties:
                operator:
                  type: string
                  enum:
                    - equals
                    - notEquals
              additionalProperties: false
            value:
              $ref: '#/components/schemas/SegmentFilterValue'
          additionalProperties: false
    SurveyElementBase:
      type: object
      required:
        - id
        - type
        - headline
        - required
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
          description: Stable element id. Avoid spaces and reserved ids.
        type:
          type: string
          enum:
            - openText
            - multipleChoiceSingle
            - multipleChoiceMulti
            - nps
            - rating
            - csat
            - ces
            - consent
            - pictureSelection
            - cta
            - date
            - fileUpload
            - cal
            - matrix
            - address
            - ranking
            - contactInfo
        headline:
          $ref: '#/components/schemas/TranslatableText'
        subheader:
          $ref: '#/components/schemas/TranslatableText'
        required:
          type: boolean
        imageUrl:
          type: string
        videoUrl:
          type: string
        isDraft:
          type: boolean
          description: Draft marker used by the editor and future update rules.
    SurveyCharLimit:
      type: object
      description: Optional `openText` character limit configuration.
      properties:
        enabled:
          type: boolean
          default: false
        min:
          type: number
        max:
          type: number
      additionalProperties: false
    SurveyValidation:
      type: object
      description: Optional element-level validation rules.
      required:
        - rules
      properties:
        logic:
          type: string
          enum:
            - and
            - or
          default: and
        rules:
          type: array
          items:
            $ref: '#/components/schemas/SurveyValidationRule'
      additionalProperties: false
    SurveyChoice:
      type: object
      required:
        - id
        - label
      properties:
        id:
          type: string
          description: Stable choice id.
        label:
          $ref: '#/components/schemas/TranslatableText'
      additionalProperties: false
    SurveyShuffleOption:
      type: string
      enum:
        - none
        - all
        - exceptLast
        - reverseOrderOccasionally
        - reverseOrderExceptLast
    SurveyMultipleChoiceDisplayType:
      type: string
      enum:
        - list
        - dropdown
    SurveyPictureChoice:
      type: object
      required:
        - id
        - imageUrl
      properties:
        id:
          type: string
          description: Stable picture choice id.
        imageUrl:
          type: string
      additionalProperties: false
    SurveyToggleInputConfig:
      type: object
      description: Field config for address and contact info elements.
      required:
        - show
        - required
        - placeholder
      properties:
        show:
          type: boolean
        required:
          type: boolean
        placeholder:
          $ref: '#/components/schemas/TranslatableText'
      additionalProperties: false
    SurveyCondition:
      type: object
      description: >
        Single condition. Operators such as `isSubmitted`, `isSkipped`,
        `isClicked`, `isAccepted`,

        `isBooked`, `isSet`, and `isEmpty` do not use `rightOperand`; comparison
        operators do.
      required:
        - id
        - leftOperand
        - operator
      properties:
        id:
          type: string
          format: cuid2
        leftOperand:
          $ref: '#/components/schemas/SurveyDynamicReference'
        operator:
          type: string
          enum:
            - equals
            - doesNotEqual
            - contains
            - doesNotContain
            - startsWith
            - doesNotStartWith
            - endsWith
            - doesNotEndWith
            - isSubmitted
            - isSkipped
            - isGreaterThan
            - isLessThan
            - isGreaterThanOrEqual
            - isLessThanOrEqual
            - equalsOneOf
            - includesAllOf
            - includesOneOf
            - doesNotIncludeOneOf
            - doesNotIncludeAllOf
            - isClicked
            - isNotClicked
            - isAccepted
            - isBefore
            - isAfter
            - isBooked
            - isPartiallySubmitted
            - isCompletelySubmitted
            - isSet
            - isNotSet
            - isEmpty
            - isNotEmpty
            - isAnyOf
        rightOperand:
          $ref: '#/components/schemas/SurveyLogicOperand'
      additionalProperties: false
    SurveyCalculateAction:
      type: object
      description: Updates a survey variable when the logic rule matches.
      required:
        - id
        - objective
        - variableId
        - operator
        - value
      properties:
        id:
          type: string
          format: cuid2
        objective:
          type: string
          enum:
            - calculate
        variableId:
          type: string
          format: cuid2
          description: Variable id for `calculate`.
        operator:
          type: string
          enum:
            - assign
            - concat
            - add
            - subtract
            - multiply
            - divide
        value:
          $ref: '#/components/schemas/SurveyLogicOperand'
      additionalProperties: false
    SurveyRequireAnswerAction:
      type: object
      description: Requires an element/question to be answered before continuing.
      required:
        - id
        - objective
        - target
      properties:
        id:
          type: string
          format: cuid2
        objective:
          type: string
          enum:
            - requireAnswer
        target:
          type: string
          description: Target element id.
      additionalProperties: false
    SurveyJumpToBlockAction:
      type: object
      description: Jumps to another block or ending when the logic rule matches.
      required:
        - id
        - objective
        - target
      properties:
        id:
          type: string
          format: cuid2
        objective:
          type: string
          enum:
            - jumpToBlock
        target:
          type: string
          format: cuid2
          description: Target block id or ending id.
      additionalProperties: false
    SegmentFilterValue:
      description: >
        Comparison value for a segment filter condition. A string or number for
        most operators; a relative-date object (`{ amount, unit }`) for
        `isOlderThan` / `isNewerThan`; a two-element `[from, to]` string array
        for `isBetween`. Ignored for `isSet` / `isNotSet` but still required.
      oneOf:
        - type: string
        - type: number
        - type: object
          required:
            - amount
            - unit
          properties:
            amount:
              type: number
            unit:
              type: string
              enum:
                - days
                - weeks
                - months
                - years
          additionalProperties: false
        - type: array
          items:
            type: string
          minItems: 2
          maxItems: 2
    SurveyValidationRule:
      type: object
      required:
        - id
        - type
        - params
      properties:
        id:
          type: string
        type:
          type: string
          enum:
            - minLength
            - maxLength
            - pattern
            - email
            - url
            - phone
            - equals
            - doesNotEqual
            - contains
            - doesNotContain
            - minValue
            - maxValue
            - isGreaterThan
            - isLessThan
            - minSelections
            - maxSelections
            - minRanked
            - rankAll
            - minRowsAnswered
            - answerAllRows
            - isLaterThan
            - isEarlierThan
            - isBetween
            - isNotBetween
            - fileExtensionIs
            - fileExtensionIsNot
        params:
          type: object
          additionalProperties: true
        field:
          type: string
          enum:
            - addressLine1
            - addressLine2
            - city
            - state
            - zip
            - country
            - firstName
            - lastName
            - email
            - phone
            - company
      additionalProperties: false
    SurveyDynamicReference:
      type: object
      description: Dynamic reference to another value in the survey document.
      required:
        - type
        - value
      properties:
        type:
          type: string
          enum:
            - element
            - variable
            - hiddenField
        value:
          type: string
          description: Element id, variable id, or hidden field id depending on `type`.
        meta:
          type: object
          additionalProperties:
            type: string
      additionalProperties: false
    SurveyLogicOperand:
      oneOf:
        - type: object
          required:
            - type
            - value
          properties:
            type:
              type: string
              enum:
                - static
            value:
              oneOf:
                - type: string
                - type: number
                - type: array
                  items:
                    type: string
          additionalProperties: false
        - $ref: '#/components/schemas/SurveyDynamicReference'
  responses:
    V3Unauthorized:
      description: Not authenticated (no valid session or API key).
      headers:
        X-Request-Id:
          schema:
            type: string
          description: Request correlation ID
        Cache-Control:
          schema:
            type: string
          example: private, no-store
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    V3TooManyRequests:
      description: Rate limit exceeded.
      headers:
        X-Request-Id:
          schema:
            type: string
          description: Request correlation ID
        Cache-Control:
          schema:
            type: string
          example: private, no-store
        Retry-After:
          schema:
            type: integer
          description: Seconds until the current rate-limit window resets.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    V3InternalServerError:
      description: Internal Server Error.
      headers:
        X-Request-Id:
          schema:
            type: string
          description: Request correlation ID
        Cache-Control:
          schema:
            type: string
          example: private, no-store
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  securitySchemes:
    sessionAuth:
      type: apiKey
      in: cookie
      name: next-auth.session-token
      description: >
        NextAuth session JWT cookie. **Development:** often
        `next-auth.session-token`.

        **Production (HTTPS):** often `__Secure-next-auth.session-token`. Send
        the cookie your browser receives after sign-in.
    apiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >
        Management API key; must include **workspaceId** as an allowed workspace
        with read, write, or manage permission.

````