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

# Update a feedback record

> Changes the answer, `language`, `user_id` or `metadata` of one record. Only the members sent
are changed; provenance (`tenant_id`, `source_*`, `field_*`, `submission_id`, `collected_at`)
is immutable. Requires an API key with **write** permission (or higher) on a workspace the
record's dataset is assigned to, and the dataset must be assigned to **exactly one** workspace
— see the delete operation for why.

Two behaviours are silent and worth knowing: `metadata` is **replaced**, not merged; and
changing `value_text` clears the enrichment members (`sentiment`, `sentiment_score`,
`emotions`, `value_text_translated`, `translation_lang_key`) and queues re-enrichment and
re-embedding — the response shows them absent until that completes. Changing `language` clears
and re-queues the translation pair only.




## OpenAPI

````yaml /api-v3-reference/openapi.yml patch /api/v3/feedbackRecords/{id}
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/feedbackRecords/{id}:
    patch:
      tags:
        - V3 Feedback Records
      summary: Update a feedback record
      description: >
        Changes the answer, `language`, `user_id` or `metadata` of one record.
        Only the members sent

        are changed; provenance (`tenant_id`, `source_*`, `field_*`,
        `submission_id`, `collected_at`)

        is immutable. Requires an API key with **write** permission (or higher)
        on a workspace the

        record's dataset is assigned to, and the dataset must be assigned to
        **exactly one** workspace

        — see the delete operation for why.


        Two behaviours are silent and worth knowing: `metadata` is **replaced**,
        not merged; and

        changing `value_text` clears the enrichment members (`sentiment`,
        `sentiment_score`,

        `emotions`, `value_text_translated`, `translation_lang_key`) and queues
        re-enrichment and

        re-embedding — the response shows them absent until that completes.
        Changing `language` clears

        and re-queues the translation pair only.
      operationId: updateFeedbackRecordV3
      parameters:
        - $ref: '#/components/parameters/FeedbackRecordIdPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateFeedbackRecordRequest'
            examples:
              correctText:
                summary: Correct a text answer
                value:
                  value_text: Queues at the north gate were far too long on match day.
              replaceMetadata:
                summary: Replace the metadata object
                value:
                  metadata:
                    channel: survey
                    stand: north
                    priority: high
      responses:
        '200':
          description: Feedback record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackRecord'
              examples:
                updated:
                  summary: >-
                    After correcting the text — enrichment members are cleared
                    pending re-enrichment
                  value:
                    id: 018e1234-5678-7abc-8ef0-123456789abd
                    tenant_id: ph7zv3w2u1x5k9d8m4q6c0bn
                    collected_at: '2026-08-15T10:30:00Z'
                    created_at: '2026-08-15T10:30:02Z'
                    updated_at: '2026-08-16T09:02:11Z'
                    source_type: survey
                    source_id: cm2k7q9x00003v8h1a2b3c4d5
                    source_name: Post-match survey
                    submission_id: cm2k7qa1z0007v8h1e6f7g8h9
                    field_id: q2
                    field_label: What could we improve?
                    field_type: text
                    value_text: Queues at the north gate were far too long on match day.
                    user_id: fan-8813
                    language: en
        '400':
          $ref: '#/components/responses/FeedbackRecordsBadRequest'
        '401':
          $ref: '#/components/responses/FeedbackRecordsUnauthorized'
        '403':
          $ref: '#/components/responses/FeedbackRecordsForbidden'
        '404':
          $ref: '#/components/responses/FeedbackRecordsRecordNotFound'
        '409':
          $ref: '#/components/responses/FeedbackRecordsWriteConflict'
        '413':
          $ref: '#/components/responses/FeedbackRecordsPayloadTooLarge'
        '500':
          $ref: '#/components/responses/FeedbackRecordsInternalServerError'
        '503':
          $ref: '#/components/responses/FeedbackRecordsServiceUnavailable'
      security:
        - apiKeyAuth: []
components:
  parameters:
    FeedbackRecordIdPath:
      in: path
      name: id
      required: true
      schema:
        type: string
        format: uuid
      description: >-
        Feedback record id (UUID). The dataset is resolved from the record
        itself, so the caller cannot choose which dataset it is authorized
        against. An id that is not a UUID answers **400**, and an id whose
        dataset the API key cannot reach answers **403** rather than 404, so ids
        cannot be probed. The gateway caches the record→dataset lookup for 60
        seconds, so for up to a minute after a delete the id is still authorized
        and the store answers **404**; after that the same id answers **403**.
  schemas:
    UpdateFeedbackRecordRequest:
      type: object
      description: >-
        A partial update. Only the members sent are changed; an empty body is
        accepted and changes nothing. Provenance (`tenant_id`, `source_*`,
        `field_*`, `submission_id`, `collected_at`) is immutable.
      additionalProperties: false
      properties:
        value_text:
          type: string
          maxLength: 30000
          description: >-
            New text answer. Changing it clears the server-generated enrichment
            members (`sentiment`, `sentiment_score`, `emotions`,
            `value_text_translated`, `translation_lang_key`), queues
            re-enrichment and re-embedding, and the response reflects the
            cleared state.
        value_id:
          type: string
          maxLength: 255
          description: New stable option id for a choice answer. Opaque, not validated.
        value_number:
          type: number
          format: double
          minimum: -1000000000000000
          maximum: 1000000000000000
          description: New numeric answer. Must be between -1e15 and +1e15.
        value_boolean:
          type: boolean
          description: New yes / no answer.
        value_date:
          type: string
          format: date-time
          description: New date answer. Must be between 1970-01-01 and 2080-12-31.
        language:
          type: string
          maxLength: 10
          description: >-
            New ISO language code. Changing it clears and re-queues the
            translation pair only.
        user_id:
          type: string
          maxLength: 255
          description: New end-user identifier.
        metadata:
          type:
            - object
            - 'null'
          additionalProperties: {}
          description: >-
            **Replaces** the stored metadata wholesale — it is not merged, so
            send every key you want to keep. Same value rules and **400**
            rejections as on create.
    FeedbackRecord:
      type: object
      description: >-
        One piece of feedback in the normalized Feedback Record shape. Field
        names are `snake_case`: the payload is the Hub's, passed through the API
        gateway unchanged. Optional members are **absent** (not `null`) when
        unset.
      additionalProperties: false
      required:
        - id
        - collected_at
        - created_at
        - updated_at
        - source_type
        - field_id
        - field_type
        - submission_id
        - tenant_id
      properties:
        id:
          type: string
          format: uuid
          description: UUIDv7 primary key.
        tenant_id:
          type: string
          format: cuid2
          description: >-
            The feedback dataset this record belongs to (the dataset id, as
            shown by the MCP `list_feedback_datasets` tool).
        collected_at:
          type: string
          format: date-time
          description: When the feedback was given.
        created_at:
          type: string
          format: date-time
          description: >-
            When the record was stored. Diverges from `collected_at` on a
            historical re-import.
        updated_at:
          type: string
          format: date-time
          description: When the record was last changed.
        source_type:
          type: string
          description: Type of feedback source, e.g. `survey`, `review`, `feedback_form`.
        source_id:
          type: string
          description: Reference to the survey / form / ticket the record came from.
        source_name:
          type: string
          description: Human-readable name of the source.
        submission_id:
          type: string
          description: >-
            Identifier of the logical submission this record belongs to (e.g. a
            response id). Records are unique per (`tenant_id`, `submission_id`,
            `field_id`).
        field_id:
          type: string
          description: Identifier of the question / field.
        field_label:
          type: string
          description: The question text.
        field_group_id:
          type: string
          description: >-
            Stable identifier grouping related fields (ranking, matrix and grid
            questions).
        field_group_label:
          type: string
          description: Human-readable question text for the group.
        field_type:
          $ref: '#/components/schemas/FeedbackRecordFieldType'
        language:
          type: string
          description: ISO language code the feedback was given in.
        user_id:
          type: string
          description: End-user identifier (e.g. an anonymous id or an email hash).
        value_text:
          type: string
          description: Text answer.
        value_id:
          type: string
          description: >-
            Stable id of the selected option in the source system (e.g. a survey
            choice id), stored alongside `value_text` so choice answers keep
            their identity across label edits and languages. Opaque. Absent for
            free-text and non-choice answers.
        value_number:
          type: number
          format: double
          description: Numeric answer (ratings, NPS scores, numbers).
        value_boolean:
          type: boolean
          description: Yes / no answer.
        value_date:
          type: string
          format: date-time
          description: Date answer.
        metadata:
          type:
            - object
            - array
            - string
            - number
            - boolean
            - 'null'
          items: {}
          description: >-
            Arbitrary context stored with the record and returned with
            equivalent values (key order and number formatting are normalized).
            Omitted when the record has no metadata; a record explicitly stored
            with a JSON `null` returns `null`. An object is the supported shape
            and the only one the write operations accept; the wider union exists
            so records written before that convention still deserialize.
        sentiment:
          $ref: '#/components/schemas/FeedbackRecordSentiment'
        sentiment_score:
          type: number
          format: double
          minimum: -1
          maximum: 1
          description: >-
            Signed sentiment polarity from `-1.0` (very negative) to `1.0` (very
            positive). Read-only; absent until the record is enriched.
        emotions:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/FeedbackRecordEmotion'
          description: >-
            Emotions inferred from `value_text`; multi-label from a fixed set.
            Read-only, and **never an empty array** — the column is either
            absent or has at least one label. Absent covers two different
            states: not yet classified, and classified with no emotion detected.
            Do not read absence as "not enriched yet"; `has_emotions=false`
            selects both, and the enrichment-status endpoint is what
            distinguishes them.
        value_text_translated:
          type: string
          description: >-
            `value_text` translated into the dataset's configured target
            language. Read-only; absent until the record is enriched.
        translation_lang_key:
          type: string
          maxLength: 35
          description: >-
            BCP-47 locale that `value_text_translated` was produced in.
            Read-only; absent until the record is enriched.
    FeedbackRecordFieldType:
      type: string
      description: >-
        The type of a feedback field, which determines which `value_*` member
        carries the answer: `text` → `value_text` (the only enrichable type),
        `categorical` → `value_text` + `value_id`, `nps` / `csat` / `ces` /
        `rating` / `number` → `value_number`, `boolean` → `value_boolean`,
        `date` → `value_date`.
      enum:
        - text
        - categorical
        - nps
        - csat
        - ces
        - rating
        - number
        - boolean
        - date
    FeedbackRecordSentiment:
      type: string
      description: >-
        Sentiment polarity label inferred from `value_text` by the sentiment
        enrichment: five ordinal levels plus a distinct `mixed`.
        Server-generated and read-only.
      enum:
        - very_negative
        - negative
        - neutral
        - positive
        - very_positive
        - mixed
    FeedbackRecordEmotion:
      type: string
      description: >-
        A single emotion label inferred from `value_text` by the emotion
        enrichment (the six Ekman basic emotions). Emotions are multi-label, so
        a record carries zero or more of these; "mixed" is not a label, it is
        two or more present at once. Server-generated and read-only.
      enum:
        - joy
        - anger
        - sadness
        - fear
        - surprise
        - disgust
    HubProblem:
      type: object
      additionalProperties: false
      description: >-
        RFC 9457 problem details (`application/problem+json`) as produced by the
        feedback store behind the API gateway. Branch on the stable `code`
        member rather than on `title` or `type`. Note the member names differ
        from the rest of v3 (`request_id`, not `requestId`) — the payload is
        passed through unchanged.
      required:
        - title
        - status
        - code
        - request_id
      properties:
        type:
          type: string
          format: uri
          description: A URI reference identifying the problem type.
        title:
          type: string
          description: >-
            Short, human-readable summary of the problem type; stable across
            occurrences.
        status:
          type: integer
          description: HTTP status code.
        code:
          type: string
          description: >-
            Stable, machine-readable error code. `service_unavailable` means a
            feature or dependency is temporarily unavailable and the request may
            be retried later; `tenant_write_conflict` means the write collided
            with an in-progress dataset purge and may be retried unchanged.
            Every other code is terminal until the request itself changes.
          enum:
            - validation
            - bad_request
            - unauthorized
            - forbidden
            - not_found
            - conflict
            - tenant_write_conflict
            - method_not_allowed
            - content_too_large
            - service_unavailable
            - internal_server_error
        detail:
          type: string
          description: Human-readable explanation specific to this occurrence.
        instance:
          type: string
          description: >-
            URI reference identifying this occurrence — the request path **as
            the feedback store saw it** (`/v1/feedback-records/...`), not the
            `/api/v3/feedbackRecords/...` path you called.
        request_id:
          type: string
          description: >-
            Identifier for this request, also returned in the `X-Request-ID`
            response header; include it when reporting a problem.
        details:
          type: object
          additionalProperties: true
          description: >-
            Optional structured context, e.g. `resource_type` on a not-found
            problem.
        invalid_params:
          type: array
          maxItems: 100
          items:
            $ref: '#/components/schemas/HubInvalidParam'
          description: Field-level validation failures. Present on `validation` problems.
    HubInvalidParam:
      type: object
      additionalProperties: false
      description: >-
        A single invalid request parameter (RFC 9457 `invalid_params` extension
        member).
      required:
        - name
        - reason
      properties:
        name:
          type: string
          description: >-
            Dotted path to the offending request field, e.g. `field_type` or
            `items[0].field_type`.
        reason:
          type: string
          description: >-
            Human- and agent-readable explanation of why the value is invalid,
            naming allowed values or constraints so the request can be corrected
            without guessing.
  responses:
    FeedbackRecordsBadRequest:
      description: >-
        Bad Request. From the **gateway** (`text/plain`): a missing or malformed
        `tenant_id`, a request body that is not JSON, or a path under
        `/api/v3/feedbackRecords` that is not one of the documented operations.
        A record id that is not a UUID and a path such as `/count` both fall in
        that last case and share the one `Unsupported FeedbackRecords route`
        body, so the two cannot be told apart from the response. From the
        **feedback store** (`application/problem+json`): schema validation
        failures — an unknown enum value, an inverted range, a malformed or
        re-ordered cursor, or a string with NULL bytes or invalid UTF-8.
      content:
        text/plain:
          schema:
            type: string
          examples:
            missingTenant:
              summary: Gateway — tenant_id missing or malformed
              value: Invalid or missing tenant_id
            unsupportedRoute:
              summary: Gateway — not a documented operation
              value: Unsupported FeedbackRecords route
        application/problem+json:
          schema:
            $ref: '#/components/schemas/HubProblem'
          examples:
            invertedRange:
              summary: Feedback store — a range filter supplied backwards
              value:
                type: https://hub.formbricks.com/problems/validation
                title: Validation Failed
                status: 400
                code: validation
                detail: One or more request parameters are invalid
                request_id: 0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b
                invalid_params:
                  - name: sentiment_score_min
                    reason: must be less than or equal to sentiment_score_max
    FeedbackRecordsUnauthorized:
      description: >-
        Unauthorized — no credentials, or an API key that does not exist or is
        revoked. Answered by the gateway as `text/plain`, before the request
        reaches the feedback store.
      content:
        text/plain:
          schema:
            type: string
          example: Unauthorized
    FeedbackRecordsForbidden:
      description: >-
        Forbidden. Answered by the gateway as `text/plain` whenever the request
        cannot be authorized: the dataset does not exist, is archived, or is not
        assigned to a workspace the API key has the required permission on; the
        organization lacks the Unify Feedback entitlement; a mutation was
        attempted with an API key on a dataset shared by more than one
        workspace; or the record id does not exist. Existence is not leaked: an
        unknown dataset or record is refused here rather than with a 404. The
        single-record operations are the one exception, and only for a minute
        after a delete — see their `404`.
      content:
        text/plain:
          schema:
            type: string
          example: Forbidden
    FeedbackRecordsRecordNotFound:
      description: >-
        Not Found from the feedback store (`application/problem+json`, `code:
        not_found`). The record was deleted after the gateway resolved its
        dataset: that lookup is cached for 60 seconds, so for up to a minute
        after a delete the call is still authorized and reaches the store, which
        no longer has the record. Once the cache entry expires the same id
        answers **403** instead. Treat both as "this record is not available to
        you".
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/HubProblem'
    FeedbackRecordsWriteConflict:
      description: >-
        Conflict (`application/problem+json`, `code: tenant_write_conflict`).
        The dataset is being **purged** while you write to it. Transient: retry
        the request unchanged once the purge completes. Only creates can
        conflict on a duplicate, so this operation never answers `code:
        conflict`.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/HubProblem'
    FeedbackRecordsPayloadTooLarge:
      description: >-
        Payload Too Large. The gateway buffers the request body in order to
        authorize it, and the proxy in front of it stops reading at 1 MiB —
        Envoy's `maxRequestBytes` on Formbricks Cloud, Traefik's `maxbodysize`
        in the Docker install. The refusal is therefore generated by that proxy
        rather than by the application, so neither its media type nor its body
        is part of this contract; branch on the status alone.
    FeedbackRecordsInternalServerError:
      description: >-
        Internal Server Error from the feedback store
        (`application/problem+json`, `code: internal_server_error`).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/HubProblem'
    FeedbackRecordsServiceUnavailable:
      description: >-
        Service Unavailable. From the **gateway** (`text/plain`): the record's
        dataset could not be resolved because the feedback store did not answer,
        the authorization service itself was unreachable, or the record's stored
        `tenant_id` is not a dataset id this API can address. From the
        **feedback store** (`application/problem+json`, `code:
        service_unavailable`): the operation needs a capability that is not
        configured on this instance — semantic search and similar records
        require an embedding model.
      content:
        text/plain:
          schema:
            type: string
          example: Feedback record lookup failed
        application/problem+json:
          schema:
            $ref: '#/components/schemas/HubProblem'
  securitySchemes:
    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.

````