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

# Update a survey

> Edit a survey. Send any subset of the survey contract. Once a survey is `active` or has any responses, changing `type`, `questions` or `settings.anonymous` is refused with `400`, because it would change what past answers mean. Duplicate the survey in the app to change those. Name, cooldown and the other settings stay editable. The merged result is validated as a whole, so a patch can fail on a rule that involves a key you did not send. Rules that compare several fields (a required first question, the NPS 0-10 and CSAT/CES scale ranges, promotional copy, `showWhen` rules) are checked after the merge, and a failure returns `400` `bad_request` with a generic message and no field detail. A value that fails its own field schema returns `422`. Requires a secret server key (`gt_live_`). Publishable keys (`gt_pub_`) return `403`.

**Scope:** `write:surveys`



## OpenAPI

````yaml /openapi.json patch /surveys/{id}
openapi: 3.1.0
info:
  title: GainTrace API
  version: 1.0.0
  description: >-
    The GainTrace REST API (v1). Read your customer data and push product-usage
    events, metric events, and signals into GainTrace.


    All endpoints are authenticated with a Bearer API key and scoped to a single
    workspace.


    Each API key can send 60 requests a minute. A `429` response includes a
    `Retry-After` header with the seconds to wait.
servers:
  - url: https://app.gaintrace.com/api/v1
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Companies
    description: Read and manage canonical company records.
  - name: Signals
    description: Read, create, and manage buyer / risk signals.
  - name: Ingest
    description: Push product-usage and metric events into GainTrace.
  - name: Contacts
    description: >-
      The original path for People. It shares its handlers with /people, and
      also offers delete-by-key.
  - name: Webhooks
    description: Subscribe to workspace events with signed, retried delivery.
  - name: People
    description: Read and manage People. One Person can belong to many Companies.
  - name: NPS responses
    description: Import NPS scores from another survey tool and read your NPS history.
  - name: Surveys
    description: >-
      Create and launch surveys, request invitations, record responses and mint
      in-app sessions.
  - name: Survey responses
    description: Read the responses collected by your surveys.
  - name: Survey segments
    description: Save groups of Companies and People that surveys can target.
  - name: Survey imports
    description: Pull responses from the survey tools your workspace has connected.
paths:
  /surveys/{id}:
    patch:
      tags:
        - Surveys
      summary: Update a survey
      description: >-
        Edit a survey. Send any subset of the survey contract. Once a survey is
        `active` or has any responses, changing `type`, `questions` or
        `settings.anonymous` is refused with `400`, because it would change what
        past answers mean. Duplicate the survey in the app to change those.
        Name, cooldown and the other settings stay editable. The merged result
        is validated as a whole, so a patch can fail on a rule that involves a
        key you did not send. Rules that compare several fields (a required
        first question, the NPS 0-10 and CSAT/CES scale ranges, promotional
        copy, `showWhen` rules) are checked after the merge, and a failure
        returns `400` `bad_request` with a generic message and no field detail.
        A value that fails its own field schema returns `422`. Requires a secret
        server key (`gt_live_`). Publishable keys (`gt_pub_`) return `403`.


        **Scope:** `write:surveys`
      operationId: updateSurvey
      parameters:
        - $ref: '#/components/parameters/surveyId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SurveyPatch'
            example:
              name: Relationship NPS, Q4
              cooldownDays: 60
      responses:
        '200':
          description: The updated survey.
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    $ref: '#/components/schemas/Survey'
              example:
                data:
                  id: 7b6f2c1e-4d3a-4b5c-9e8f-1a2b3c4d5e6f
                  name: Relationship NPS, Q4
                  measure: nps
                  status: draft
                  questions:
                    - text: How likely are you to recommend us to a colleague?
                      type: rating
                      required: true
                    - text: What is the main reason for your score?
                      type: text
                      required: false
                  settings:
                    thankYouTitle: Thank you
                    reminders:
                      afterDays: 3
                      max: 1
                  cooldownDays: 60
                  createdAt: '2026-10-14T09:00:00.000Z'
                  updatedAt: '2026-10-14T09:00:00.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
components:
  parameters:
    surveyId:
      name: id
      in: path
      required: true
      description: The survey's UUID.
      schema:
        type: string
        format: uuid
  schemas:
    SurveyPatch:
      type: object
      additionalProperties: false
      description: >-
        Any subset of the survey contract. `settings` keys you send replace the
        matching top-level settings keys, and the rest are kept. A nested object
        such as `reminders` is replaced as a whole.
      properties:
        name:
          type: string
          maxLength: 255
          minLength: 1
        type:
          $ref: '#/components/schemas/SurveyMeasure'
        questions:
          type: array
          minItems: 1
          maxItems: 20
          items:
            $ref: '#/components/schemas/SurveyQuestion'
        settings:
          $ref: '#/components/schemas/SurveySettings'
        cooldownDays:
          type: integer
          minimum: 0
          maximum: 730
          description: >-
            Minimum days since this person last got a survey of the same measure
            (an email sent, an invited answer or an in-app view) before this
            survey can invite them or show to them in-app. It doesn't block
            responses you record through the API.
    Survey:
      type: object
      required:
        - id
        - name
        - measure
        - status
        - questions
        - settings
        - cooldownDays
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        measure:
          $ref: '#/components/schemas/SurveyMeasure'
        status:
          type: string
          enum:
            - draft
            - active
            - closed
        questions:
          type: array
          items:
            $ref: '#/components/schemas/SurveyQuestion'
        settings:
          $ref: '#/components/schemas/SurveySettings'
        cooldownDays:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    SurveyMeasure:
      type: string
      enum:
        - nps
        - csat
        - ces
        - custom
      description: >-
        `nps` is a 0-10 scale. `csat` and `ces` use a 1-5 or 1-7 scale. `custom`
        has no band and its responses are `unscored`. For `nps`, `csat` and
        `ces`, the first question must be a required `rating` or `scale`
        question: it carries the score.
    SurveyQuestion:
      type: object
      required:
        - text
        - type
        - required
      additionalProperties: false
      properties:
        text:
          type: string
          minLength: 1
          maxLength: 1000
          description: >-
            The question text. Use `{{company}}`, `{{first_name}}` or
            `{{score}}` to fill in details from a signed link. `{{company|your
            team}}` falls back to the text after the bar. Surveys must stay
            purely feedback, so promotional phrases such as "discount code" or
            "limited time" are rejected.
        type:
          type: string
          enum:
            - rating
            - scale
            - text
            - multiple_choice
            - multi_select
            - dropdown
            - matrix
            - ranking
            - date
            - statement
            - consent
          description: >-
            `rating` and `scale` are scored questions. `multiple_choice` picks
            one answer, `multi_select` picks several (checkboxes) and `dropdown`
            picks one from a long list. `matrix` asks one scale for several
            rows, `ranking` orders a list, `date` takes a calendar date,
            `statement` shows text with no answer, and `consent` is a required
            checkbox.
        required:
          type: boolean
          description: >-
            A `statement` can't be required. A `consent` question must be
            required.
        options:
          type: array
          minItems: 2
          maxItems: 100
          items:
            type: string
            maxLength: 255
            minLength: 1
          description: >-
            Answer choices, each different. Required for `multiple_choice`,
            `multi_select`, `dropdown` and `ranking`. Up to 20 for
            `multiple_choice` and `multi_select`, 100 for `dropdown`, and 10 for
            `ranking`. For `matrix`, these are the scale columns, lowest first,
            up to 10.
        rows:
          type: array
          minItems: 2
          maxItems: 15
          items:
            type: string
            maxLength: 255
            minLength: 1
          description: >-
            `matrix` only: the statements people rate against the columns in
            `options`. Each row is different.
        minSelections:
          type: integer
          minimum: 1
          maximum: 20
          description: >-
            `multi_select` only: the fewest boxes a person must tick. It can't
            exceed `maxSelections` or the number of options.
        maxSelections:
          type: integer
          minimum: 1
          maximum: 20
          description: >-
            `multi_select` only: the most boxes a person may tick. It can't
            exceed the number of options.
        scaleMin:
          type: integer
          minimum: 0
          maximum: 10
        scaleMax:
          type: integer
          minimum: 1
          maximum: 10
          description: Must be greater than `scaleMin` when both are set.
        scaleLabels:
          type: object
          required:
            - min
            - max
          additionalProperties: false
          properties:
            min:
              type: string
              maxLength: 100
            max:
              type: string
              maxLength: 100
        display:
          type: string
          enum:
            - faces
            - stars
            - numbers
          description: >-
            `rating` only. `stars` needs a 1 to 5 scale. `faces` is for the
            first question of a CSAT survey on a 1 to 5 scale. NPS and CES
            scores show as numbers.
        description:
          type: string
          maxLength: 2000
          description: '`statement` and `consent` only: the text under the question.'
        link:
          type: object
          required:
            - label
            - url
          additionalProperties: false
          description: >-
            `statement` and `consent` only: an optional link, for example to a
            policy.
          properties:
            label:
              type: string
              minLength: 1
              maxLength: 40
            url:
              type: string
              format: uri
              maxLength: 2048
              description: An HTTPS link.
        preset:
          type: string
          enum:
            - pmf
          description: >-
            `pmf` marks the product-market fit question. It must be a
            `multiple_choice` question with the four standard answers `Very
            disappointed`, `Somewhat disappointed`, `Not disappointed` and `I no
            longer use it`, in that order. Results then include the share who
            would be very disappointed.
        showWhen:
          type: object
          required:
            - operator
            - value
          additionalProperties: false
          description: >-
            Show this question only when an earlier answer matches. A question
            can depend only on an earlier question. The operator depends on the
            earlier question's type: `lte`, `gte` or `eq` with a number for
            `rating` and `scale`; `is` or `is_not` with one of the answer
            choices for `multiple_choice` and `dropdown`; `includes` or
            `excludes` with one of the choices for `multi_select`; `before` or
            `after` with a `YYYY-MM-DD` date for `date`. A score value must sit
            inside the earlier question's scale.
          properties:
            question:
              type: integer
              minimum: 0
              maximum: 19
              description: >-
                Zero-based index of the earlier question. Defaults to the first
                question.
            operator:
              type: string
              enum:
                - lte
                - gte
                - eq
                - is
                - is_not
                - includes
                - excludes
                - before
                - after
            value:
              oneOf:
                - type: integer
                  minimum: 0
                  maximum: 10
                - type: string
                  maxLength: 255
                  minLength: 1
        logic:
          allOf:
            - $ref: '#/components/schemas/SurveyLogicGroup'
          description: >-
            Show this question only when these conditions on earlier answers or
            hidden fields match. Never send both `logic` and `showWhen`.
        jumps:
          type: array
          maxItems: 10
          items:
            $ref: '#/components/schemas/SurveyJumpRule'
          description: >-
            Where to go after this question. The first rule whose conditions
            match wins.
        libraryId:
          type: string
          format: uuid
          description: >-
            The workspace library question this one was copied from. Never shown
            to respondents.
    SurveySettings:
      type: object
      additionalProperties: false
      description: Optional survey settings. Unknown keys are rejected.
      properties:
        defaultLanguage:
          type: string
          enum:
            - en
            - ar
            - bg
            - bn
            - ca
            - cs
            - da
            - de
            - el
            - es
            - et
            - fa
            - fi
            - fil
            - fr
            - he
            - hi
            - hr
            - hu
            - id
            - it
            - ja
            - ko
            - lt
            - lv
            - ms
            - nl
            - 'no'
            - pl
            - pt-BR
            - pt-PT
            - ro
            - ru
            - sk
            - sl
            - sr
            - sv
            - th
            - tr
            - uk
            - ur
            - vi
            - zh-CN
            - zh-TW
          description: >-
            The language respondents see when nothing else matches. Defaults to
            `en`.
        translations:
          type: object
          maxProperties: 15
          propertyNames:
            enum:
              - en
              - ar
              - bg
              - bn
              - ca
              - cs
              - da
              - de
              - el
              - es
              - et
              - fa
              - fi
              - fil
              - fr
              - he
              - hi
              - hr
              - hu
              - id
              - it
              - ja
              - ko
              - lt
              - lv
              - ms
              - nl
              - 'no'
              - pl
              - pt-BR
              - pt-PT
              - ro
              - ru
              - sk
              - sl
              - sr
              - sv
              - th
              - tr
              - uk
              - ur
              - vi
              - zh-CN
              - zh-TW
          additionalProperties:
            $ref: '#/components/schemas/SurveyLanguageCopy'
          description: >-
            Respondent copy per language code, up to 15 languages. A respondent
            sees the language they choose or their browser asks for when the
            survey offers it, and the default language otherwise. Translations
            never change a score or answer: GainTrace maps what a respondent
            picked back to the default-language choice before it saves.
        brandColor:
          type: string
          pattern: ^#[0-9a-fA-F]{6}$
          example: '#FFAC00'
        logoUrl:
          type: string
          format: uri
          maxLength: 2048
          description: An HTTPS image URL.
        thankYouTitle:
          type: string
          maxLength: 120
        thankYouMessage:
          type: string
          maxLength: 1000
        thankYouByBand:
          type: object
          additionalProperties: false
          description: >-
            Replaces the title, message and button for respondents whose score
            lands in a band.
          properties:
            negative:
              type: object
              additionalProperties: false
              properties:
                title:
                  type: string
                  maxLength: 120
                message:
                  type: string
                  maxLength: 1000
                link:
                  type: object
                  required:
                    - label
                    - url
                  additionalProperties: false
                  description: >-
                    A button for this score group only, such as a review site
                    for promoters. Falls back to `thankYouLink`.
                  properties:
                    label:
                      type: string
                      minLength: 1
                      maxLength: 40
                    url:
                      type: string
                      format: uri
                      maxLength: 2048
                      description: An HTTPS link.
            neutral:
              type: object
              additionalProperties: false
              properties:
                title:
                  type: string
                  maxLength: 120
                message:
                  type: string
                  maxLength: 1000
                link:
                  type: object
                  required:
                    - label
                    - url
                  additionalProperties: false
                  description: >-
                    A button for this score group only, such as a review site
                    for promoters. Falls back to `thankYouLink`.
                  properties:
                    label:
                      type: string
                      minLength: 1
                      maxLength: 40
                    url:
                      type: string
                      format: uri
                      maxLength: 2048
                      description: An HTTPS link.
            positive:
              type: object
              additionalProperties: false
              properties:
                title:
                  type: string
                  maxLength: 120
                message:
                  type: string
                  maxLength: 1000
                link:
                  type: object
                  required:
                    - label
                    - url
                  additionalProperties: false
                  description: >-
                    A button for this score group only, such as a review site
                    for promoters. Falls back to `thankYouLink`.
                  properties:
                    label:
                      type: string
                      minLength: 1
                      maxLength: 40
                    url:
                      type: string
                      format: uri
                      maxLength: 2048
                      description: An HTTPS link.
        thankYouLink:
          type: object
          required:
            - label
            - url
          additionalProperties: false
          description: An optional next step on the thank-you screen.
          properties:
            label:
              type: string
              minLength: 1
              maxLength: 40
            url:
              type: string
              format: uri
              maxLength: 2048
              description: An HTTPS link.
        anonymous:
          type: boolean
          description: >-
            Anonymous surveys store no person, company or respondent identity on
            a response. This setting locks once the survey is live or has
            responses.
        hiddenFields:
          type: array
          maxItems: 10
          items:
            type: string
            pattern: ^[a-z][a-z0-9_]{0,39}$
          description: >-
            Names of link parameters saved with each response, for example
            `plan`. Values for any other name are dropped.
        endings:
          type: array
          maxItems: 10
          items:
            $ref: '#/components/schemas/SurveyEnding'
          description: Named thank-you screens that only logic jumps reach.
        customCss:
          type: string
          maxLength: 10000
          description: >-
            Styling for the respondent page and the in-app survey. It can't
            contain `<`, `@import` or `url()`.
        sendWindow:
          type: object
          required:
            - startHour
            - endHour
            - weekdaysOnly
          additionalProperties: false
          description: >-
            Approved email invitations wait for these local hours in the
            workspace time zone.
          properties:
            startHour:
              type: integer
              minimum: 0
              maximum: 23
            endHour:
              type: integer
              minimum: 1
              maximum: 24
              description: Must be greater than `startHour`.
            weekdaysOnly:
              type: boolean
        reminders:
          type: object
          required:
            - afterDays
            - max
          additionalProperties: false
          description: >-
            Re-send to people who have not answered. People who answered are
            never reminded.
          properties:
            afterDays:
              type: integer
              minimum: 1
              maximum: 14
            max:
              type: integer
              minimum: 1
              maximum: 3
        alerts:
          type: object
          required:
            - bands
          additionalProperties: false
          description: >-
            Post each new response in the chosen bands to Slack, Microsoft
            Teams, or both. Set `slackChannel`, `teamsWebhookUrl` or both.
          properties:
            bands:
              type: array
              minItems: 1
              maxItems: 3
              items:
                type: string
                enum:
                  - negative
                  - neutral
                  - positive
            slackChannel:
              type: string
              pattern: ^[CG][A-Z0-9]{6,}$
              description: A Slack channel id.
            teamsWebhookUrl:
              type: string
              format: uri
              maxLength: 2048
              description: The HTTPS webhook URL from a Teams Workflow.
        inApp:
          type: object
          required:
            - enabled
            - allowedOrigins
            - minAgeDays
            - minSessions
            - maxDisplays
            - delayMs
          additionalProperties: false
          description: In-app collection rules. See the in-app SDK guide.
          properties:
            enabled:
              type: boolean
              description: Enabling requires at least one allowed origin.
            allowedOrigins:
              type: array
              maxItems: 20
              items:
                type: string
                format: uri
                maxLength: 255
              description: >-
                Exact origins such as `https://app.example.com`. No paths, no
                wildcards. HTTPS only, except `http://localhost` and
                `http://127.0.0.1`.
            minAgeDays:
              type: integer
              minimum: 0
              maximum: 730
              description: Days since the person was first recorded in GainTrace.
            minSessions:
              type: integer
              minimum: 1
              maximum: 365
              description: >-
                Distinct UTC days on which a session was recorded for the
                person.
            maxDisplays:
              type: integer
              minimum: 1
              maximum: 10
              description: Maximum displays inside the survey's cooldown window.
            delayMs:
              type: integer
              minimum: 0
              maximum: 120000
            samplingPercent:
              type: integer
              minimum: 1
              maximum: 100
              description: >-
                Show to this share of eligible people. The same people stay in
                or out of the sample on every visit. Omit for everyone.
            position:
              type: string
              enum:
                - bottom
                - bottom-right
                - bottom-left
                - center
              description: Defaults to `bottom`. `center` is a blocking modal.
            targeting:
              $ref: '#/components/schemas/SurveyInAppTargeting'
        advocacy:
          type: object
          additionalProperties: false
          required:
            - enabled
            - ask
          description: >-
            When a respondent in the top score group (NPS 9-10, or the top two
            points of a CSAT or effort scale) at a known Company answers, the
            Company's CSM gets a task to ask them for a review, a case study or
            a reference call. At most one open ask per person; anonymous surveys
            never create one.
          properties:
            enabled:
              type: boolean
            ask:
              type: string
              enum:
                - review
                - case_study
                - reference
        aiProbe:
          type: object
          required:
            - enabled
          additionalProperties: false
          description: >-
            One optional AI-written follow-up question, asked after the response
            is saved and labelled as AI-generated. Off unless enabled. When a
            respondent answers it, the question and answer appear in the
            response `answers` under `_probe`.
          properties:
            enabled:
              type: boolean
    Error:
      type: object
      required:
        - error
      description: The standard error envelope. Every 4xx/5xx response uses this shape.
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - bad_request
                - unauthorized
                - payment_required
                - forbidden
                - not_found
                - conflict
                - unprocessable_entity
                - rate_limit_exceeded
                - internal_server_error
                - service_unavailable
                - gateway_timeout
              description: >-
                Stable, machine-readable error code. Each code maps to exactly
                one HTTP status.
            message:
              type: string
              description: >-
                Human-readable, safe description of what went wrong. Never
                contains internal or provider detail.
            doc_url:
              type: string
              format: uri
              example: https://docs.gaintrace.com/errors#not_found
              description: Deep link to this code in the Errors reference.
    SurveyLogicGroup:
      type: object
      required:
        - match
        - conditions
      additionalProperties: false
      description: >-
        Conditions on earlier answers or hidden fields. `match` is `all` or
        `any`.
      properties:
        match:
          type: string
          enum:
            - all
            - any
        conditions:
          type: array
          minItems: 1
          maxItems: 10
          items:
            oneOf:
              - type: object
                required:
                  - question
                  - operator
                  - value
                additionalProperties: false
                description: >-
                  An earlier answer. `question` is the zero-based index. `band`
                  compares the score group (negative, neutral or positive).
                properties:
                  question:
                    type: integer
                    minimum: 0
                    maximum: 19
                  operator:
                    type: string
                    enum:
                      - lte
                      - gte
                      - eq
                      - is
                      - is_not
                      - includes
                      - excludes
                      - before
                      - after
                      - band
                  value:
                    oneOf:
                      - type: integer
                        minimum: 0
                        maximum: 10
                      - type: string
                        maxLength: 255
                        minLength: 1
              - type: object
                required:
                  - hidden
                  - operator
                  - value
                additionalProperties: false
                description: A hidden field the survey lists in `settings.hiddenFields`.
                properties:
                  hidden:
                    type: string
                    pattern: ^[a-z][a-z0-9_]{0,39}$
                  operator:
                    type: string
                    enum:
                      - is
                      - is_not
                  value:
                    type: string
                    minLength: 1
                    maxLength: 200
    SurveyJumpRule:
      type: object
      required:
        - when
        - to
      additionalProperties: false
      description: >-
        Where to go after the question when the conditions match. The first
        matching rule wins, otherwise the next question.
      properties:
        when:
          $ref: '#/components/schemas/SurveyLogicGroup'
        to:
          oneOf:
            - type: object
              required:
                - question
              additionalProperties: false
              properties:
                question:
                  type: integer
                  minimum: 1
                  maximum: 19
            - type: object
              required:
                - ending
              additionalProperties: false
              properties:
                ending:
                  type: string
                  pattern: ^[a-z0-9][a-z0-9_-]{0,39}$
                  description: The `id` of an entry in `settings.endings`.
            - type: object
              required:
                - end
              additionalProperties: false
              properties:
                end:
                  type: boolean
                  enum:
                    - true
    SurveyLanguageCopy:
      type: object
      additionalProperties: false
      description: >-
        The copy for one language. Anything you leave out falls back to the
        default language. A question translation applies only while the default
        copy it was written against is unchanged: GainTrace stamps a `source`
        fingerprint on save, and a translation whose fingerprint no longer
        matches is ignored until you update it.
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 200
          description: The survey name shown as the heading.
        questions:
          type: array
          maxItems: 20
          description: >-
            One entry per question, in order. Use `null` for a question you
            don't translate.
          items:
            oneOf:
              - type: 'null'
              - type: object
                additionalProperties: false
                properties:
                  source:
                    type: string
                    pattern: ^[0-9a-f]{8}$
                    description: >-
                      Fingerprint of the default copy. GainTrace sets it on save
                      when you leave it out.
                  text:
                    type: string
                    minLength: 1
                    maxLength: 1000
                  description:
                    type: string
                    minLength: 1
                    maxLength: 2000
                  linkLabel:
                    type: string
                    maxLength: 255
                    minLength: 1
                  options:
                    type: array
                    maxItems: 100
                    items:
                      type: string
                      maxLength: 255
                      minLength: 1
                    description: Same length and order as the default `options`.
                  rows:
                    type: array
                    maxItems: 15
                    items:
                      type: string
                      maxLength: 255
                      minLength: 1
                    description: Same length and order as the default `rows`.
                  scaleLabels:
                    type: object
                    additionalProperties: false
                    properties:
                      min:
                        type: string
                        minLength: 1
                        maxLength: 100
                      max:
                        type: string
                        minLength: 1
                        maxLength: 100
        thankYou:
          type: object
          additionalProperties: false
          properties:
            title:
              type: string
              minLength: 1
              maxLength: 120
            message:
              type: string
              minLength: 1
              maxLength: 1000
            linkLabel:
              type: string
              minLength: 1
              maxLength: 40
            byBand:
              type: object
              additionalProperties: false
              properties:
                negative:
                  type: object
                  additionalProperties: false
                  properties:
                    title:
                      type: string
                      minLength: 1
                      maxLength: 120
                    message:
                      type: string
                      minLength: 1
                      maxLength: 1000
                    linkLabel:
                      type: string
                      minLength: 1
                      maxLength: 40
                neutral:
                  type: object
                  additionalProperties: false
                  properties:
                    title:
                      type: string
                      minLength: 1
                      maxLength: 120
                    message:
                      type: string
                      minLength: 1
                      maxLength: 1000
                    linkLabel:
                      type: string
                      minLength: 1
                      maxLength: 40
                positive:
                  type: object
                  additionalProperties: false
                  properties:
                    title:
                      type: string
                      minLength: 1
                      maxLength: 120
                    message:
                      type: string
                      minLength: 1
                      maxLength: 1000
                    linkLabel:
                      type: string
                      minLength: 1
                      maxLength: 40
        ui:
          type: object
          additionalProperties:
            type: string
            minLength: 1
            maxLength: 1000
          description: >-
            Overrides for the respondent page's interface strings, keyed by
            string id. An unknown key returns `422`.
        endings:
          type: object
          description: Translations of `settings.endings`, keyed by ending `id`.
          additionalProperties:
            type: object
            additionalProperties: false
            properties:
              title:
                type: string
                minLength: 1
                maxLength: 120
              message:
                type: string
                minLength: 1
                maxLength: 1000
              linkLabel:
                type: string
                minLength: 1
                maxLength: 40
        reviewedAt:
          type: string
          format: date-time
    SurveyEnding:
      type: object
      required:
        - id
        - name
      additionalProperties: false
      description: >-
        A named thank-you screen that only logic jumps reach. It overrides the
        score-group and default copy field by field.
      properties:
        id:
          type: string
          pattern: ^[a-z0-9][a-z0-9_-]{0,39}$
        name:
          type: string
          minLength: 1
          maxLength: 60
        title:
          type: string
          maxLength: 120
        message:
          type: string
          maxLength: 1000
        link:
          type: object
          required:
            - label
            - url
          additionalProperties: false
          properties:
            label:
              type: string
              minLength: 1
              maxLength: 40
            url:
              type: string
              format: uri
              maxLength: 2048
              description: An HTTPS link.
    SurveyInAppTargeting:
      type: object
      additionalProperties: false
      description: >-
        Who, where and when the in-app survey shows. GainTrace checks the page
        rules first, then the audience, then the trigger. People who don't
        qualify get a reason from `POST /surveys/{id}/session`.
      properties:
        audience:
          description: Limit the survey to a saved segment, or to rules you send here.
          oneOf:
            - type: object
              required:
                - segmentId
              additionalProperties: false
              properties:
                segmentId:
                  type: string
                  format: uuid
            - type: object
              required:
                - rules
              additionalProperties: false
              properties:
                rules:
                  $ref: '#/components/schemas/SurveyAudienceRules'
        pages:
          type: object
          required:
            - include
            - exclude
          additionalProperties: false
          description: >-
            Page rules matched against the `context.url` your backend sends when
            it mints a session. With include rules, the page must match one. A
            page that matches an exclude rule never shows the survey.
          properties:
            include:
              type: array
              maxItems: 20
              items:
                $ref: '#/components/schemas/SurveyPageRule'
            exclude:
              type: array
              maxItems: 20
              items:
                $ref: '#/components/schemas/SurveyPageRule'
        trigger:
          description: >-
            What makes the survey appear. Without a trigger it shows as soon as
            the person qualifies.
          oneOf:
            - type: object
              required:
                - kind
              additionalProperties: false
              properties:
                kind:
                  type: string
                  enum:
                    - page_load
            - type: object
              required:
                - kind
                - events
              additionalProperties: false
              properties:
                kind:
                  type: string
                  enum:
                    - event
                events:
                  type: array
                  minItems: 1
                  maxItems: 10
                  items:
                    type: string
                    pattern: ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$
                  description: Event names your code sends with `GaintraceSurveys.track()`.
            - type: object
              required:
                - kind
                - seconds
              additionalProperties: false
              properties:
                kind:
                  type: string
                  enum:
                    - time_on_page
                seconds:
                  type: integer
                  minimum: 1
                  maximum: 600
    SurveyAudienceRules:
      type: object
      required:
        - company
        - person
      additionalProperties: false
      description: >-
        Who to reach: People who match `person`, at Companies that match
        `company`. An empty `rules` list in a group matches everyone for that
        group. A request needs at least one rule across both groups, so nobody
        is ever invited by accident. Only People with an email address at
        Companies that aren't archived can match.
      properties:
        company:
          $ref: '#/components/schemas/SurveyFilterGroup'
        person:
          $ref: '#/components/schemas/SurveyFilterGroup'
    SurveyPageRule:
      type: object
      required:
        - match
        - value
      additionalProperties: false
      properties:
        match:
          type: string
          enum:
            - equals
            - contains
            - starts_with
        value:
          type: string
          minLength: 1
          maxLength: 512
          description: >-
            A path and query such as `/billing`. No spaces, and nothing after
            `#`.
    SurveyFilterGroup:
      type: object
      required:
        - logic
        - rules
      additionalProperties: false
      properties:
        logic:
          type: string
          enum:
            - and
            - or
        rules:
          type: array
          maxItems: 20
          items:
            $ref: '#/components/schemas/SurveyFilterRule'
    SurveyFilterRule:
      type: object
      required:
        - field
        - operator
        - value
      additionalProperties: false
      properties:
        field:
          type: string
          minLength: 1
          maxLength: 120
          description: >-
            In a Company rule group, a field key from your Company data model,
            such as `segment` or `plan`. In a People rule group, one of `email`,
            `name`, `role`, `contactRole`, `isChampion`, `lastSeenAt`,
            `engagementScore`, `createdAt`, or `traits.<key>` for a Person
            trait. `person.` and `people.` prefixes are accepted. Any other
            People field returns `422`.
        operator:
          type: string
          enum:
            - eq
            - neq
            - gt
            - gte
            - lt
            - lte
            - contains
            - not_contains
            - starts_with
            - ends_with
            - is_empty
            - is_not_empty
            - in
            - not_in
            - between
            - before
            - after
            - in_the_last
            - in_the_next
            - older_than
            - further_than
            - is
            - is_not
            - is_any_of
            - is_none_of
          description: The same operators Flow conditions use.
        value:
          oneOf:
            - type: string
              maxLength: 500
            - type: number
            - type: boolean
            - type: array
              maxItems: 50
              items:
                type: string
                maxLength: 200
        value2:
          oneOf:
            - type: string
              maxLength: 500
            - type: number
          description: The upper bound for `between`.
        unit:
          type: string
          enum:
            - minutes
            - hours
            - days
            - workdays
            - weeks
            - months
          description: The unit for date operators such as `in_the_last`.
  responses:
    BadRequest:
      description: The request could not be parsed (for example, malformed JSON).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: bad_request
              message: Invalid JSON body
              doc_url: https://docs.gaintrace.com/errors#bad_request
    Unauthorized:
      description: Missing, malformed, expired, or revoked API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: unauthorized
              message: Missing Authorization header
              doc_url: https://docs.gaintrace.com/errors#unauthorized
    Forbidden:
      description: >-
        The key is valid but lacks the required scope, the origin is not
        allowed, or API v1 is not enabled for the workspace.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: forbidden
              message: 'Insufficient scope: write:companies required'
              doc_url: https://docs.gaintrace.com/errors#forbidden
    NotFound:
      description: >-
        The resource, or a resource you referenced, was not found in this
        workspace.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: not_found
              message: Company not found
              doc_url: https://docs.gaintrace.com/errors#not_found
    UnprocessableEntity:
      description: The request was well-formed but a value failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: unprocessable_entity
              message: 'Request validation failed. name: name is required'
              doc_url: https://docs.gaintrace.com/errors#unprocessable_entity
    RateLimited:
      description: >-
        The per-key rate limit was exceeded. Each API key can send 60 requests
        per minute, counted in calendar-minute windows. The `Retry-After` header
        gives the whole seconds (1 to 60) until the next window opens. Wait that
        long, then retry.
      headers:
        Retry-After:
          description: Whole seconds until the next one-minute window starts.
          schema:
            type: integer
            minimum: 1
            maximum: 60
          example: 23
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: rate_limit_exceeded
              message: >-
                Can't accept more than 60 requests a minute from one API key.
                Retry in 23 seconds.
              doc_url: https://docs.gaintrace.com/errors#rate_limit_exceeded
    InternalServerError:
      description: >-
        An unexpected error occurred on our side. The failure is logged; retry
        shortly.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: internal_server_error
              message: Something went wrong. Try again in a moment.
              doc_url: https://docs.gaintrace.com/errors#internal_server_error
    ServiceUnavailable:
      description: >-
        A backing service (for example, the database) was temporarily
        unavailable. Retry shortly.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: service_unavailable
              message: We had trouble reaching the database. Try again in a moment.
              doc_url: https://docs.gaintrace.com/errors#service_unavailable
    GatewayTimeout:
      description: >-
        The request took too long upstream before completing. Retry, and reduce
        batch size for large payloads.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: gateway_timeout
              message: The request timed out. Try again in a moment.
              doc_url: https://docs.gaintrace.com/errors#gateway_timeout
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        Bearer API key. Secret server keys start with `gt_live_`; client-safe
        browser keys start with `gt_pub_` (write-only, origin-pinned). Create
        and scope keys in Settings → API Keys.

````

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