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

# Update a prompt

> Update a prompt's countries, platforms, tags or active flag.

Text is identity: a body carrying `text` is 400 `text_immutable`; an empty patch is 400 `validation_error` (`empty_patch`). `active: true` on an inactive prompt REACTIVATES it — cap-checked like a create (422) and refused (409 `conflict`) if an active prompt with the same text now exists in the brand. `active: false` deactivates (never cap-checked). On a prompt of an archived brand only a body whose SOLE field is `active: false` is accepted (409 `brand_archived` otherwise — a mixed body such as `{ active: false, tags: [...] }` is refused whole, nothing persisted).



## OpenAPI

````yaml /api-reference/openapi.json patch /orgs/{orgId}/prompts/{promptId}
openapi: 3.1.0
info:
  title: Surfais API
  version: 1.0.0
  description: >-
    The Surfais API gives programmatic access to the AI-visibility data Surfais
    measures for your brands: organisations, brands and competitors, tracked
    prompts, per-run results and mentions, scores, share of voice and cited
    sources. It can also provision brands, manage prompts and competitors,
    request on-demand scans, and deliver signed webhooks when a scan completes
    or a score or sentiment threshold is crossed.


    Base URL: `https://api.surfais.com/v1`. Send your API key as a Bearer token
    (`Authorization: Bearer sfs_live_…`). Test keys (`sfs_test_…`) are rejected
    by the production API. Requests and responses are JSON: a success is `{
    "data": … }`, an error is `{ "error": { "code", "message" }, "request_id"
    }`.


    Guides for authentication, pagination, errors, rate limits, idempotency and
    webhooks are at https://docs.surfais.com/api/introduction.
  contact:
    name: Surfais support
    email: support@surfais.com
    url: https://docs.surfais.com/api/support
servers:
  - url: https://api.surfais.com/v1
    description: >-
      Production (the `/v1` prefix is part of the server URL; paths below are
      relative to it).
security:
  - bearerKey: []
tags:
  - name: orgs
    description: Organisations reachable by the key, their usage, and the partner link.
  - name: brands
    description: Own brands (properties) and competitor rows.
  - name: prompts
    description: 'Tracked prompts: reads, CRUD and the declarative sync.'
  - name: results
    description: Finalised run-level data.
  - name: scores
    description: Persisted scores, per-market history and share of voice.
  - name: sources
    description: Cited domains.
  - name: scans
    description: On-demand scan requests.
  - name: webhooks
    description: >-
      Outbound event delivery: endpoints, subscriptions, deliveries (partner
      keys).
externalDocs:
  description: Guides, concepts and webhooks
  url: https://docs.surfais.com/api/introduction
paths:
  /orgs/{orgId}/prompts/{promptId}:
    patch:
      tags:
        - prompts
      summary: Update a prompt
      description: >-
        Update a prompt's countries, platforms, tags or active flag.


        Text is identity: a body carrying `text` is 400 `text_immutable`; an
        empty patch is 400 `validation_error` (`empty_patch`). `active: true` on
        an inactive prompt REACTIVATES it — cap-checked like a create (422) and
        refused (409 `conflict`) if an active prompt with the same text now
        exists in the brand. `active: false` deactivates (never cap-checked). On
        a prompt of an archived brand only a body whose SOLE field is `active:
        false` is accepted (409 `brand_archived` otherwise — a mixed body such
        as `{ active: false, tags: [...] }` is refused whole, nothing
        persisted).
      operationId: updatePrompt
      parameters:
        - in: path
          name: orgId
          schema:
            type: string
            format: uuid
            pattern: >-
              ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
            description: >-
              Organisation id. Org keys: the key's own org. Partner keys: any
              org with an active link. Anything else is 404.
          required: true
          description: >-
            Organisation id. Org keys: the key's own org. Partner keys: any org
            with an active link. Anything else is 404.
        - in: path
          name: promptId
          schema:
            type: string
            format: uuid
            pattern: >-
              ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
            description: Prompt id within the org; 404 when not in the org.
          required: true
          description: Prompt id within the org; 404 when not in the org.
        - in: header
          name: Idempotency-Key
          schema:
            description: >-
              Optional. 1–255 characters, unique per intended write. Same key +
              same request → the stored response is replayed with
              `Idempotent-Replayed: true`; same key + different request → 409
              `idempotency_key_reuse`; still running → 409
              `idempotency_key_in_flight`. Outside that range → 400
              `validation_error` (`invalid_header`).
            type: string
            minLength: 1
            maxLength: 255
          description: >-
            Optional. 1–255 characters, unique per intended write. Same key +
            same request → the stored response is replayed with
            `Idempotent-Replayed: true`; same key + different request → 409
            `idempotency_key_reuse`; still running → 409
            `idempotency_key_in_flight`. Outside that range → 400
            `validation_error` (`invalid_header`).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PromptPatch'
      responses:
        '200':
          description: The updated prompt.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
            Idempotent-Replayed:
              description: >-
                Present, as `true`, when this response was replayed from the
                Idempotency-Key store rather than executed.
              schema:
                description: >-
                  Present, as `true`, when this response was replayed from the
                  Idempotency-Key store rather than executed.
                type: string
                const: 'true'
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Prompt'
                required:
                  - data
        '400':
          description: >-
            `validation_error` (with `details`), `invalid_json`, or the
            operation's own 400 (`text_immutable`, `duplicate_prompt_text`). An
            `Idempotency-Key` outside its length bounds is `validation_error`
            with `details[].path = "Idempotency-Key"`.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
            Idempotent-Replayed:
              description: >-
                Present, as `true`, when this response was replayed from the
                Idempotency-Key store rather than executed.
              schema:
                description: >-
                  Present, as `true`, when this response was replayed from the
                  Idempotency-Key store rather than executed.
                type: string
                const: 'true'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: '`invalid_api_key` — uniform for every authentication failure.'
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: >-
            `write_scope_required` (the key has no `write` scope),
            `link_scope_insufficient` (partner key on a `read_only` link),
            `partner_only` (`PATCH …/link` with an org key), or
            `tier_not_entitled`. Every one of them is raised after the rate
            limiter has charged the key's minute allowance, so it carries the
            `X-RateLimit-*` trio (an exhausted allowance answers 429 first).
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
            Idempotent-Replayed:
              description: >-
                Present, as `true`, when this response was replayed from the
                Idempotency-Key store rather than executed.
              schema:
                description: >-
                  Present, as `true`, when this response was replayed from the
                  Idempotency-Key store rather than executed.
                type: string
                const: 'true'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: >-
            `not_found` — the org is not reachable by this key or a nested id is
            not in it. Raised after authentication and the minute charge, so it
            carries the `X-RateLimit-*` trio and is metered.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
            Idempotent-Replayed:
              description: >-
                Present, as `true`, when this response was replayed from the
                Idempotency-Key store rather than executed.
              schema:
                description: >-
                  Present, as `true`, when this response was replayed from the
                  Idempotency-Key store rather than executed.
                type: string
                const: 'true'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: >-
            `conflict` — reactivation collides with an active prompt of the same
            text (`ExistingResourceConflict`); `brand_archived` — the prompt's
            brand is archived and the patch is not exactly `{ active: false }`
            (`BrandStateConflict`); or `idempotency_key_reuse` /
            `idempotency_key_in_flight`, with no `details`.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
            Idempotent-Replayed:
              description: >-
                Present, as `true`, when this response was replayed from the
                Idempotency-Key store rather than executed.
              schema:
                description: >-
                  Present, as `true`, when this response was replayed from the
                  Idempotency-Key store rather than executed.
                type: string
                const: 'true'
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        description: >-
                          Stable machine code — see the error list in the
                          description.
                      message:
                        type: string
                      details:
                        anyOf:
                          - $ref: '#/components/schemas/ExistingResourceConflict'
                          - $ref: '#/components/schemas/BrandStateConflict'
                    required:
                      - code
                      - message
                  request_id:
                    type: string
                    description: Echoed in the `X-Request-Id` header.
                required:
                  - error
                  - request_id
        '413':
          description: '`payload_too_large` — the body exceeds 1 MB.'
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '422':
          description: >-
            `prompt_cap_exceeded` / `country_cap_exceeded` — a plan cap refused
            the write; `details` is a `CapRefusal`. Its fields are all optional:
            they are lifted out of the database's refusal text, so a cap without
            a tier carries fewer of them.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
            Idempotent-Replayed:
              description: >-
                Present, as `true`, when this response was replayed from the
                Idempotency-Key store rather than executed.
              schema:
                description: >-
                  Present, as `true`, when this response was replayed from the
                  Idempotency-Key store rather than executed.
                type: string
                const: 'true'
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        description: >-
                          Stable machine code — see the error list in the
                          description.
                      message:
                        type: string
                      details:
                        $ref: '#/components/schemas/CapRefusal'
                    required:
                      - code
                      - message
                  request_id:
                    type: string
                    description: Echoed in the `X-Request-Id` header.
                required:
                  - error
                  - request_id
        '429':
          description: >-
            `rate_limited` (per-minute limit, or too many failed authentications
            from this address) or `quota_exceeded`.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            Retry-After:
              required: true
              description: Seconds to wait before retrying — present on every 429 and 503.
              schema:
                type: string
                description: >-
                  Seconds to wait before retrying — present on every 429 and
                  503.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '500':
          description: >-
            `internal_error` — quote `request_id`. A failure inside a route
            answers after the minute charge and carries the `X-RateLimit-*`
            trio; a failure before the limiter carries only `X-Request-Id`.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              description: >-
                Present when the failure occurred after the minute charge
                (inside a route); absent on a failure before the limiter.
                Requests allowed per minute for this key.
              schema:
                description: >-
                  Present when the failure occurred after the minute charge
                  (inside a route); absent on a failure before the limiter.
                  Requests allowed per minute for this key.
                type: string
            X-RateLimit-Remaining:
              description: >-
                Present when the failure occurred after the minute charge.
                Requests left in the current minute window.
              schema:
                description: >-
                  Present when the failure occurred after the minute charge.
                  Requests left in the current minute window.
                type: string
            X-RateLimit-Reset:
              description: >-
                Present when the failure occurred after the minute charge. Unix
                seconds at which the current minute window ends.
              schema:
                description: >-
                  Present when the failure occurred after the minute charge.
                  Unix seconds at which the current minute window ends.
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '503':
          description: >-
            `api_unavailable` (switched off; `Retry-After` is 60),
            `rate_limit_unavailable` (fail-closed limiter — its store, or a
            saturated failed-authentication gate; before the minute charge, so
            no `X-RateLimit-*` trio), or `store_unavailable` (a tenant-store
            call behind an authenticated request exceeded 15000 ms — raised
            after the minute charge, so it is counted and carries the trio).
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            Retry-After:
              required: true
              description: Seconds to wait before retrying — present on every 429 and 503.
              schema:
                type: string
                description: >-
                  Seconds to wait before retrying — present on every 429 and
                  503.
            X-RateLimit-Limit:
              description: >-
                Present when the failure occurred after the minute charge
                (inside a route); absent on a failure before the limiter.
                Requests allowed per minute for this key.
              schema:
                description: >-
                  Present when the failure occurred after the minute charge
                  (inside a route); absent on a failure before the limiter.
                  Requests allowed per minute for this key.
                type: string
            X-RateLimit-Remaining:
              description: >-
                Present when the failure occurred after the minute charge.
                Requests left in the current minute window.
              schema:
                description: >-
                  Present when the failure occurred after the minute charge.
                  Requests left in the current minute window.
                type: string
            X-RateLimit-Reset:
              description: >-
                Present when the failure occurred after the minute charge. Unix
                seconds at which the current minute window ends.
              schema:
                description: >-
                  Present when the failure occurred after the minute charge.
                  Unix seconds at which the current minute window ends.
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
components:
  schemas:
    PromptPatch:
      type: object
      properties:
        countries:
          description: Markets the prompt runs in. Duplicates are collapsed.
          minItems: 1
          maxItems: 29
          type: array
          items:
            description: >-
              ISO 3166-1 alpha-2 market (case-insensitive). Must be one Surfais
              scans; otherwise 400 `validation_error` with `details[].code =
              "unsupported_country"`.
            type: string
            pattern: ^[A-Za-z]{2}$
        platforms:
          description: Platforms the prompt runs on. Duplicates are collapsed.
          minItems: 1
          maxItems: 5
          type: array
          items:
            type: string
            enum:
              - perplexity
              - chatgpt
              - gemini
              - claude
              - ai_overviews
            description: Scanned AI platform id.
        tags:
          description: >-
            Free-text labels, each at most 100 characters and 256 UTF-8 bytes
            after trimming, with no control characters. Duplicates are
            collapsed.
          maxItems: 50
          type: array
          items:
            type: string
            minLength: 1
            maxLength: 100
        active:
          description: >-
            `true` on an inactive prompt REACTIVATES it (cap-checked: 422
            `prompt_cap_exceeded` / `country_cap_exceeded`, 409 `conflict` if an
            active prompt with the same text exists in the brand); `false`
            deactivates. On a prompt of an archived brand `false` is accepted
            only as the SOLE field of the body — sent with any other field the
            whole patch is 409 `brand_archived`.
          type: boolean
    Prompt:
      type: object
      properties:
        id:
          type: string
          format: uuid
          pattern: >-
            ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
          description: UUID.
        brand_id:
          type: string
          format: uuid
          pattern: >-
            ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
          description: UUID.
        text:
          type: string
        tags:
          type: array
          items:
            type: string
          description: >-
            Free-text labels, as stored — trimmed, never blank, de-duplicated,
            and never longer than 256 bytes or carrying a control character
            (enforced on write, so every tag here is one `?tag=` accepts). CASE
            IS PRESERVED EXACTLY: nothing folds a tag on any write path, so
            `Launch` and `launch` are two different tags. Rows written before
            that enforcement shipped can still hold an untrimmed or duplicated
            label, which is why `?tag=` compares the token exactly as sent.
            Treat a tag as an opaque string and pass it back verbatim.
        countries:
          type: array
          items:
            type: string
          description: >-
            Markets this prompt runs in, as stored — always upper-cased, never
            empty strings, de-duplicated, and never longer than 256 bytes or
            carrying a control character (enforced on write, so every token here
            is one `?country=` accepts). ISO 3166-1 alpha-2 for every prompt
            written through /v1 or the dashboard's country pickers; the `OTHER`
            geography sentinel and other legacy tokens can appear on rows
            written by onboarding, the suggest/expand flows or CSV import,
            because the stored list carries no SHAPE constraint. Treat a token
            as opaque and pass it back verbatim.
        platforms:
          type: array
          items:
            type: string
        active:
          type: boolean
        source:
          type: string
          enum:
            - ui
            - api
            - onboarding
            - csv
            - suggested
            - gsc
          description: >-
            Provenance — never null: every prompt carries one of the listed
            values, `ui` by default. `api` = written through /v1, the only value
            that pairs with `created_by_key_id`.
        created_by_key_id:
          anyOf:
            - type: string
              format: uuid
              pattern: >-
                ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
              description: UUID.
            - type: 'null'
          description: The API key that created the prompt, when it was created via /v1.
        created_at:
          type: string
          description: RFC 3339 timestamp.
          format: date-time
        updated_at:
          type: string
          description: RFC 3339 timestamp.
          format: date-time
      required:
        - id
        - brand_id
        - text
        - tags
        - countries
        - platforms
        - active
        - source
        - created_by_key_id
        - created_at
        - updated_at
    ErrorEnvelope:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: Stable machine code — see the error list in the description.
            message:
              type: string
            details: {}
          required:
            - code
            - message
        request_id:
          type: string
          description: Echoed in the `X-Request-Id` header.
      required:
        - error
        - request_id
    ExistingResourceConflict:
      type: object
      properties:
        existing_id:
          type: string
          format: uuid
          pattern: >-
            ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
          description: The row that already holds this domain, external_ref or prompt text.
      required:
        - existing_id
    BrandStateConflict:
      type: object
      properties:
        brand_id:
          type: string
          format: uuid
          pattern: >-
            ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
          description: The own brand whose state refused the write.
      required:
        - brand_id
    CapRefusal:
      type: object
      properties:
        tier:
          type: string
        cap:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        attempted:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
  securitySchemes:
    bearerKey:
      type: http
      scheme: bearer
      bearerFormat: sfs_live_… / sfs_test_…
      description: >-
        A Surfais API key — an organisation key or a partner key. Issued by
        Surfais; shown once. Rotate by creating a new key, then revoking the old
        one.

````