{
  "openapi": "3.1.0",
  "info": {
    "title": "StateSet Chat Widget API",
    "version": "1.0.6",
    "description": "Reviewed self-hosted Response widget server contract: chat, conversation capabilities, storefront sessions, commerce, analytics, native operator inbox and site administration. The example host is a placeholder for your deployment."
  },
  "servers": [
    {
      "url": "https://api.example.com",
      "description": "Your widget API deployment; replace this placeholder."
    }
  ],
  "x-stateset-provenance": {
    "reviewedAt": "2026-10-20",
    "repository": "stateset/response-chat-widget",
    "revision": "1cacaaa",
    "workspaceVersion": "1.2.13",
    "sourceOpenApiVersion": "1.0.0",
    "source": "docs/openapi.widget.yaml checked against server-openai.js; admin/router.js and liveChat.js handlers",
    "workingTree": "Includes local uncommitted session, execution, native live-chat, admin compatibility and release work. Review establishes the checked source contract, not published assets or hosted rollout.",
    "scope": "Generic GET/POST/PUT/PATCH/DELETE routes and admin handlers. Account-specific eligibility flow omitted. OPTIONS /api/widget/settings is served but not rendered as an operation page. OAuth discovery and MCP use their own protocols.",
    "normalization": "Reusable request bodies/responses and shared path parameters expanded for the page generator. Literal schemas have explicit primitive types. Current handler corrections include live-chat acknowledgements/operator sender metadata, text_replace, strict-reporting mode, 409 execution conflicts, and public-settings lookup outcomes."
  },
  "components": {
    "securitySchemes": {
      "widgetToken": {
        "type": "apiKey",
        "in": "header",
        "name": "x-widget-token",
        "description": "Signed widget token (`orgId.agentId.timestamp.signature` or the site-bound\n`orgId.agentId.timestamp.siteId.signature`; see scripts/generate-widget-token.js).\nAlso read from `x-widget-auth` and `x-widget-auth-token`. Optional when the server has\nno `STATESET_WIDGET_AUTH_SECRET`, or when `STATESET_WIDGET_AUTH_REQUIRED=false`\n(legacy tokenless grace).\n"
      },
      "widgetBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "The same widget token sent as `Authorization: Bearer <token>` (or `Authorization: Widget <token>`)."
      },
      "threadToken": {
        "type": "apiKey",
        "in": "header",
        "name": "x-widget-thread-token",
        "description": "Per-thread capability disclosed once when the thread is created. Also accepted as\n`x-thread-token`, `?threadToken=` or `threadToken` in the JSON body. Not required for\nlegacy threads that never had one issued, unless the server runs with\n`STATESET_THREAD_TOKEN_REQUIRED=true` (default `false`): those threads then answer\n`403 { error: thread_forbidden }` on every thread, chat, feedback and cart-discount path.\n"
      },
      "metricsToken": {
        "type": "apiKey",
        "in": "header",
        "name": "x-metrics-token"
      },
      "gorgiasWebhookToken": {
        "type": "apiKey",
        "in": "query",
        "name": "token"
      },
      "shopifyWebhookHmac": {
        "type": "apiKey",
        "in": "header",
        "name": "x-shopify-hmac-sha256",
        "description": "Base64 HMAC-SHA256 of the raw body under the Shopify webhook secret."
      },
      "admin_key": {
        "type": "http",
        "scheme": "bearer",
        "description": "`rw_admin_<hex>` for the admin API"
      }
    },
    "parameters": {
      "ThreadId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Thread id (`thr_<uuid>`).",
        "schema": {
          "type": "string"
        }
      },
      "OrgIdQuery": {
        "name": "orgId",
        "in": "query",
        "required": false,
        "deprecated": true,
        "description": "Ignored by the analytics routes (telemetry, response review, thread analytics): their org comes only from the verified widget token, and they answer `401 unauthorized` without one (outside production, `STATESET_ORG_ID` is a logged fallback). Kept for older clients.",
        "schema": {
          "type": "string"
        }
      },
      "AgentIdQuery": {
        "name": "agentId",
        "in": "query",
        "required": false,
        "description": "Agent id hint (also `agent_id`, or header `x-agent-id`). Overridden by the widget token when present; must match it.",
        "schema": {
          "type": "string"
        }
      },
      "ProductQuery": {
        "name": "q",
        "in": "query",
        "required": false,
        "description": "Search terms. Without it the full list is returned.",
        "schema": {
          "type": "string"
        }
      },
      "ProductLimit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "ReviewLimit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Records to analyze; default 100, max 500.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 500
        }
      },
      "IncludeRecords": {
        "name": "includeRecords",
        "in": "query",
        "required": false,
        "description": "`true` keeps the analyzed records in `analysis.records`.",
        "schema": {
          "type": "string",
          "enum": [
            "true",
            "false"
          ]
        }
      },
      "ThreadAnalyticsLimit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Records to analyze; default 5000, max 10000.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10000
        }
      },
      "IncludeThreads": {
        "name": "includeThreads",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "enum": [
            "true",
            "false"
          ]
        }
      },
      "SampleLimit": {
        "name": "sampleLimit",
        "in": "query",
        "required": false,
        "schema": {
          "type": "integer",
          "default": 5
        }
      },
      "AnalyticsRange": {
        "name": "range",
        "in": "query",
        "required": false,
        "description": "`<n>d` for the last n days (1-365) or `all`; omitted means no lower bound.",
        "schema": {
          "type": "string",
          "pattern": "^([1-9]\\d{0,2}d|all)$"
        }
      }
    },
    "requestBodies": {
      "ChatRequest": {
        "required": true,
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ChatRequest"
            }
          }
        }
      },
      "ThreadCreateRequest": {
        "required": false,
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ThreadCreateRequest"
            }
          }
        }
      },
      "ThreadUpdateRequest": {
        "required": true,
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ThreadUpdateRequest"
            }
          }
        }
      },
      "ThreadMessageRequest": {
        "required": true,
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ThreadMessageRequest"
            }
          }
        }
      },
      "FeedbackRequest": {
        "required": true,
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/FeedbackRequest"
            }
          }
        }
      },
      "CartAddRequest": {
        "required": true,
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/CartAddRequest"
            }
          }
        }
      }
    },
    "responses": {
      "ChatReply": {
        "description": "Assistant reply. `application/json` when `stream` is falsy; `text/event-stream` when\n`stream: true` (see `SSEStream`).\n",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ChatResponse"
            }
          },
          "text/event-stream": {
            "schema": {
              "$ref": "#/components/schemas/SSEStream"
            }
          }
        }
      },
      "ChatBadRequest": {
        "description": "No message or attachments, attachments rejected, or malformed JSON",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "messageRequired": {
                "value": {
                  "error": "message required"
                }
              },
              "invalidAttachment": {
                "value": {
                  "error": "invalid_attachment"
                }
              },
              "badJson": {
                "value": {
                  "error": "bad_request",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        }
      },
      "ThreadMessageBadRequest": {
        "description": "No message or attachments, attachments rejected, or malformed JSON",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "messageRequired": {
                "value": {
                  "error": "message required"
                }
              },
              "invalidAttachment": {
                "value": {
                  "error": "invalid_attachment"
                }
              }
            }
          }
        }
      },
      "ChatServerError": {
        "description": "Provider failure. The chat handler answers `{ error: <provider error message>, type }`\n(a human-readable message, not a code) or `{ error: '<PROVIDER>_API_KEY is not set.' }`\nwhen the selected provider has no key. The request and the provider key are validated\nbefore a thread is restored or created; when a later failure happens on a thread this\nrequest created or restored, the body also carries `threadId` and the one-time\n`threadToken`. Errors thrown before the handler answer `{ error: internal_error,\nrequestId }`. On an SSE stream the failure is emitted as an `error` event instead and\nthe stream ends.\n",
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/Error"
                },
                {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Provider error message."
                    },
                    "type": {
                      "type": "string",
                      "description": "Provider error type; defaults to `api_error`."
                    },
                    "threadId": {
                      "type": "string",
                      "description": "Present when this request created or restored the thread."
                    },
                    "threadToken": {
                      "type": "string",
                      "description": "The thread's capability token, disclosed with `threadId`."
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "ThreadCreated": {
        "description": "Thread created; `threadToken` is disclosed only here.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "threadId",
                "threadToken",
                "thread"
              ],
              "properties": {
                "threadId": {
                  "type": "string"
                },
                "threadToken": {
                  "type": "string",
                  "description": "32-character base64url capability; echo it as `x-widget-thread-token`."
                },
                "thread": {
                  "$ref": "#/components/schemas/Thread"
                }
              }
            }
          }
        }
      },
      "ThreadFetched": {
        "description": "Thread and its messages",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "thread",
                "messages"
              ],
              "properties": {
                "thread": {
                  "$ref": "#/components/schemas/Thread"
                },
                "messages": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Message"
                  }
                },
                "escalated": {
                  "const": true,
                  "description": "Present when a Gorgias ticket exists for the thread.",
                  "type": "boolean"
                }
              }
            }
          }
        }
      },
      "ThreadUpdated": {
        "description": "Updated thread",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "thread"
              ],
              "properties": {
                "thread": {
                  "$ref": "#/components/schemas/Thread"
                }
              }
            }
          }
        }
      },
      "ThreadDeleted": {
        "description": "Deletion outcome (`ok: false` when the id was unknown)",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "ok"
              ],
              "properties": {
                "ok": {
                  "type": "boolean"
                }
              }
            }
          }
        }
      },
      "FeedbackAccepted": {
        "description": "Reaction stored",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "ok",
                "threadId",
                "messageId",
                "reaction"
              ],
              "properties": {
                "ok": {
                  "const": true,
                  "type": "boolean"
                },
                "threadId": {
                  "type": "string"
                },
                "messageId": {
                  "type": "string"
                },
                "reaction": {
                  "$ref": "#/components/schemas/MessageReaction"
                }
              }
            }
          }
        }
      },
      "InvalidFeedback": {
        "description": "Body failed validation (missing ids, bad reaction, comment over 500 chars)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "invalid_feedback"
            }
          }
        }
      },
      "ProductList": {
        "description": "Products",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "ok",
                "products"
              ],
              "properties": {
                "ok": {
                  "const": true,
                  "type": "boolean"
                },
                "products": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Product"
                  }
                }
              }
            }
          }
        }
      },
      "CartAdded": {
        "description": "Line added to the Shopify cart",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/CartAddResponse"
            }
          }
        }
      },
      "CartAddFailed": {
        "description": "Shopify not configured, missing variant, or Shopify user error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/CartAddError"
            }
          }
        }
      },
      "TelemetrySnapshot": {
        "description": "Telemetry snapshot",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "ok",
                "generatedAt",
                "telemetry"
              ],
              "properties": {
                "ok": {
                  "const": true,
                  "type": "boolean"
                },
                "generatedAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "telemetry": {
                  "$ref": "#/components/schemas/TelemetrySnapshot"
                }
              }
            }
          }
        }
      },
      "TelemetryDisabled": {
        "description": "Telemetry disabled on this deployment",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "telemetry_disabled"
            }
          }
        }
      },
      "ResponseReview": {
        "description": "Review report",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ResponseReviewReport"
            }
          }
        }
      },
      "ReviewSkipped": {
        "description": "Review could not run",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/OkError"
            },
            "examples": {
              "missingConfig": {
                "value": {
                  "ok": false,
                  "error": "missing_graphql_config"
                }
              }
            }
          }
        }
      },
      "ReviewQueryFailed": {
        "description": "Responses store query failed",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/OkError"
            },
            "example": {
              "ok": false,
              "error": "responses_query_failed"
            }
          }
        }
      },
      "ThreadAnalytics": {
        "description": "Thread analytics report",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ThreadAnalyticsReport"
            }
          }
        }
      },
      "ThreadAnalyticsBadRequest": {
        "description": "Bad `range`, or analytics could not run",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/OkError"
            },
            "examples": {
              "range": {
                "value": {
                  "ok": false,
                  "error": "invalid_analytics_range"
                }
              },
              "missingConfig": {
                "value": {
                  "ok": false,
                  "error": "missing_graphql_config"
                }
              }
            }
          }
        }
      },
      "ThreadAnalyticsQueryFailed": {
        "description": "Responses store query failed",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/OkError"
            },
            "example": {
              "ok": false,
              "error": "response_thread_analytics_query_failed"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Widget token missing, malformed, expired, from the future, or badly signed",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "authRequired": {
                "value": {
                  "error": "auth_required"
                }
              },
              "invalidToken": {
                "value": {
                  "error": "invalid_token"
                }
              },
              "tokenExpired": {
                "value": {
                  "error": "token_expired"
                }
              },
              "tokenInFuture": {
                "value": {
                  "error": "token_in_future"
                }
              },
              "invalidSignature": {
                "value": {
                  "error": "invalid_signature"
                }
              }
            }
          }
        }
      },
      "ForbiddenIdentity": {
        "description": "Request org/agent disagrees with the widget token claims",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "org": {
                "value": {
                  "error": "org_mismatch"
                }
              },
              "agent": {
                "value": {
                  "error": "agent_mismatch"
                }
              }
            }
          }
        }
      },
      "ForbiddenThreadOrIdentity": {
        "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "thread": {
                "value": {
                  "error": "thread_forbidden"
                }
              },
              "org": {
                "value": {
                  "error": "org_mismatch"
                }
              },
              "agent": {
                "value": {
                  "error": "agent_mismatch"
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Unknown thread (or, for feedback, a message that is not an assistant message)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "not_found"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "Body over the JSON limit, or an attachment over 10 MB",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "body": {
                "value": {
                  "error": "payload_too_large",
                  "requestId": "3f1c..."
                }
              },
              "attachment": {
                "value": {
                  "error": "payload_too_large"
                }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests for this IP (scoped by verified org/agent)",
        "headers": {
          "Retry-After": {
            "description": "Seconds until the window resets.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitedError"
            }
          }
        }
      },
      "InternalError": {
        "description": "Unhandled error",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "internal_error",
              "requestId": "3f1c..."
            }
          }
        }
      }
    },
    "headers": {
      "RequestId": {
        "description": "Request id (echoed from the inbound `x-request-id` when present).",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "ChatRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ChatRequestBase"
          },
          {
            "type": "object",
            "properties": {
              "subject": {
                "type": "string",
                "description": "Subject for a newly created thread."
              },
              "threadId": {
                "type": "string",
                "description": "Continue an existing thread; omitted or unknown creates one."
              }
            }
          }
        ]
      },
      "ChatResponse": {
        "type": "object",
        "description": "Non-streaming reply.",
        "required": [
          "ok",
          "threadId",
          "messageId",
          "reply",
          "provider",
          "model"
        ],
        "properties": {
          "ok": {
            "const": true,
            "type": "boolean"
          },
          "threadId": {
            "type": "string"
          },
          "messageId": {
            "type": "string",
            "pattern": "^msg_[0-9a-z]+_[0-9a-f]{8}$",
            "description": "Id the server stored for this AI reply in the thread (`msg_<base36 ms>_<8 hex>`).\nUse it as `messageId` for `POST /api/response/feedback`.\n",
            "example": "msg_mfu3x2k1_9c1e4a7b"
          },
          "threadToken": {
            "type": "string",
            "description": "Present only on the response that created (or restored) the thread."
          },
          "reply": {
            "type": "string"
          },
          "provider": {
            "$ref": "#/components/schemas/Provider"
          },
          "model": {
            "type": "string"
          },
          "sources": {
            "type": "array",
            "description": "Present only when `STATESET_SHOW_SOURCES=true` and the KB matched.",
            "items": {
              "$ref": "#/components/schemas/SourceCitation"
            }
          },
          "type": {
            "type": "string",
            "description": "Rich message type when `content` is present.",
            "enum": [
              "product_cards",
              "order_cards",
              "card",
              "buttons",
              "form"
            ]
          },
          "content": {
            "$ref": "#/components/schemas/RichContent"
          },
          "escalated": {
            "const": true,
            "description": "The message triggered a live-agent escalation (Gorgias ticket).",
            "type": "boolean"
          }
        },
        "additionalProperties": true
      },
      "Message": {
        "type": "object",
        "description": "A stored thread message as returned by the server (`appendMessage`).",
        "required": [
          "id",
          "sender",
          "text",
          "timestamp"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "`msg_<base36 timestamp>`"
          },
          "sender": {
            "type": "string",
            "enum": [
              "user",
              "ai",
              "agent"
            ]
          },
          "text": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "type": {
            "$ref": "#/components/schemas/MessageType"
          },
          "richContent": {
            "$ref": "#/components/schemas/RichContent"
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attachment"
            }
          },
          "toolCalls": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ToolCall"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SourceCitation"
            }
          },
          "suggestions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "thinking": {
            "type": "string"
          },
          "reaction": {
            "$ref": "#/components/schemas/MessageReaction"
          },
          "feedbackComment": {
            "type": "string",
            "maxLength": 500
          },
          "feedbackAt": {
            "type": "string",
            "format": "date-time"
          },
          "authorName": {
            "type": "string",
            "description": "Operator display name for native human replies."
          },
          "operatorEvent": {
            "type": "string",
            "enum": [
              "joined",
              "released",
              "resolved"
            ]
          }
        },
        "additionalProperties": true
      },
      "CartAddResponse": {
        "type": "object",
        "required": [
          "ok",
          "success",
          "variantId",
          "quantity",
          "cartId",
          "cartItemCount",
          "checkoutUrl",
          "discountApplied"
        ],
        "properties": {
          "ok": {
            "const": true,
            "type": "boolean"
          },
          "success": {
            "const": true,
            "type": "boolean"
          },
          "variantId": {
            "type": "string"
          },
          "quantity": {
            "type": "integer"
          },
          "cartId": {
            "type": "string"
          },
          "cartItemCount": {
            "type": "integer"
          },
          "subtotal": {
            "type": [
              "string",
              "number",
              "null"
            ]
          },
          "total": {
            "type": [
              "string",
              "number",
              "null"
            ]
          },
          "checkoutUrl": {
            "type": "string",
            "format": "uri"
          },
          "discountApplied": {
            "type": "boolean"
          },
          "discountError": {
            "type": "string"
          },
          "discountIgnored": {
            "const": true,
            "type": "boolean"
          }
        },
        "additionalProperties": true
      },
      "Error": {
        "type": "object",
        "description": "Standard error body. Known codes: `auth_required`, `invalid_token`, `token_expired`,\n`token_in_future`, `invalid_signature`, `org_mismatch`, `agent_mismatch`,\n`thread_forbidden`, `not_found`, `invalid_feedback`, `invalid_attachment`,\n`payload_too_large`, `bad_request`, `internal_error`, `rate_limited`,\n`telemetry_disabled`, `unauthorized`, `forbidden`, `webhook_not_configured`.\n`requestId` is present on bodies produced by the final error handler (413/4xx/500).\n",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "requestId": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Site": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "platform": {
            "type": "string"
          },
          "domain": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SiteList": {
        "type": "object",
        "properties": {
          "sites": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Site"
            }
          }
        }
      },
      "SiteConfig": {
        "type": "object",
        "properties": {
          "draft": {
            "type": "object"
          },
          "published": {
            "type": "object"
          }
        }
      },
      "AdminKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "key": {
            "type": "string",
            "description": "Returned once, on creation"
          },
          "orgId": {
            "type": "string"
          },
          "label": {
            "type": "string"
          }
        }
      },
      "AdminKeyCreate": {
        "type": "object",
        "properties": {
          "orgId": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "bootstrapSecret": {
            "type": "string",
            "description": "Only when minting the first key for a new org, with no bearer"
          }
        },
        "required": [
          "orgId"
        ]
      },
      "Ok": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ]
      },
      "Provider": {
        "type": "string",
        "enum": [
          "openai",
          "anthropic",
          "gemini"
        ]
      },
      "StoreKind": {
        "type": "string",
        "description": "Thread store backend.",
        "enum": [
          "memory",
          "redis"
        ]
      },
      "Readiness": {
        "type": "object",
        "required": [
          "ready",
          "store"
        ],
        "properties": {
          "ready": {
            "type": "boolean"
          },
          "store": {
            "$ref": "#/components/schemas/StoreKind"
          }
        }
      },
      "OkError": {
        "type": "object",
        "description": "Error body used by the commerce/analytics/webhook handlers (`ok: false`).",
        "required": [
          "ok",
          "error"
        ],
        "properties": {
          "ok": {
            "const": false,
            "type": "boolean"
          },
          "error": {
            "type": "string"
          }
        }
      },
      "RateLimitedError": {
        "type": "object",
        "required": [
          "error",
          "message",
          "retryAfterMs"
        ],
        "properties": {
          "error": {
            "const": "rate_limited",
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "retryAfterMs": {
            "type": "integer"
          }
        }
      },
      "Attachment": {
        "type": "object",
        "description": "Visitor attachment. Max 5 per message, 10 MB each; `type` must be one of\nimage/jpeg, image/png, image/gif, image/webp, application/pdf, text/plain, text/csv.\n`dataUrl`, when present, must be a base64 data URL of an allowed type.\n",
        "required": [
          "name",
          "size",
          "type"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "size": {
            "type": "integer",
            "maximum": 10485760
          },
          "type": {
            "type": "string"
          },
          "dataUrl": {
            "type": "string",
            "description": "`data:<mime>;base64,<payload>`"
          },
          "previewUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string",
            "description": "Public URL of an already uploaded image (read for vision providers)."
          }
        }
      },
      "ToolCall": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "arguments": {
            "type": "object",
            "additionalProperties": true
          },
          "result": {},
          "status": {
            "type": "string",
            "enum": [
              "running",
              "complete",
              "error"
            ]
          }
        }
      },
      "SourceCitation": {
        "type": "object",
        "description": "Knowledge-base source.",
        "required": [
          "id",
          "title",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "snippet": {
            "type": "string",
            "description": "Up to 220 characters of the document."
          }
        }
      },
      "MessageReaction": {
        "type": [
          "string",
          "null"
        ],
        "enum": [
          "up",
          "down",
          null
        ]
      },
      "MessageType": {
        "type": "string",
        "enum": [
          "text",
          "tool_call",
          "tool_result",
          "card",
          "buttons",
          "form",
          "product_cards",
          "order_cards",
          "citation",
          "thinking"
        ]
      },
      "ProductAction": {
        "type": "object",
        "required": [
          "type",
          "label"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "view",
              "add_to_cart",
              "bundle",
              "checkout",
              "custom"
            ]
          },
          "label": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "discountCode": {
            "type": "string"
          }
        }
      },
      "ProductVariantOption": {
        "type": "object",
        "required": [
          "label"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "swatchColor": {
            "type": "string"
          },
          "price": {
            "type": "string"
          },
          "available": {
            "type": "boolean"
          },
          "url": {
            "type": "string"
          }
        }
      },
      "ProductCard": {
        "type": "object",
        "description": "Product rendered by the widget's product_cards rich message (src/types/messages.ts).",
        "required": [
          "id",
          "title"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "price": {
            "type": "string"
          },
          "image": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "whyItMatches": {
            "type": "string"
          },
          "rating": {
            "type": "number"
          },
          "reviewCount": {
            "type": "integer"
          },
          "badges": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "isUpsell": {
            "type": "boolean"
          },
          "actions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductAction"
            }
          },
          "variantOptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductVariantOption"
            }
          },
          "variantSelectionLabel": {
            "type": "string"
          },
          "variantSelectionPlaceholder": {
            "type": "string"
          },
          "variantSelectionRequired": {
            "type": "boolean"
          },
          "variantDisplay": {
            "type": "string",
            "enum": [
              "select",
              "swatches"
            ]
          },
          "selectedVariantId": {
            "type": "string"
          },
          "selectedVariantLabel": {
            "type": "string"
          },
          "complementaryProducts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductCard"
            }
          },
          "complementaryHeadline": {
            "type": "string"
          },
          "complementaryText": {
            "type": "string"
          },
          "continueShoppingAfterAdd": {
            "type": "boolean"
          }
        }
      },
      "OrderCard": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "amount": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "trackingUrl": {
            "type": "string"
          },
          "lineItems": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "price": {
                  "type": "string"
                },
                "quantity": {
                  "type": [
                    "integer",
                    "string"
                  ]
                },
                "image": {
                  "type": "string"
                }
              }
            }
          },
          "actions": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "label"
              ],
              "properties": {
                "label": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                },
                "href": {
                  "type": "string"
                },
                "target": {
                  "type": "string",
                  "enum": [
                    "_self",
                    "_blank"
                  ]
                }
              }
            }
          }
        }
      },
      "RichContent": {
        "type": "object",
        "description": "Payload of a rich message (`card`, `buttons`, `form`, `product_cards`, `order_cards`);\nwhich keys are present depends on the message type. Mirrors `RichContent` in\nsrc/types/messages.ts minus client-only callbacks.\n",
        "properties": {
          "image": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "formId": {
            "type": "string"
          },
          "buttonStyle": {
            "type": "string",
            "enum": [
              "default",
              "pill"
            ]
          },
          "layout": {
            "type": "string",
            "enum": [
              "scroll",
              "stack"
            ]
          },
          "actions": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "label"
              ],
              "properties": {
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "buttons": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "label"
              ],
              "properties": {
                "label": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                },
                "href": {
                  "type": "string"
                },
                "target": {
                  "type": "string",
                  "enum": [
                    "_self",
                    "_blank"
                  ]
                }
              }
            }
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "label"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                },
                "placeholder": {
                  "type": "string"
                },
                "required": {
                  "type": "boolean"
                },
                "options": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "label",
                      "value"
                    ],
                    "properties": {
                      "label": {
                        "type": "string"
                      },
                      "value": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "submitLabel": {
            "type": "string"
          },
          "headline": {
            "type": "string"
          },
          "subhead": {
            "type": "string"
          },
          "products": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductCard"
            }
          },
          "autoAction": {
            "type": "object",
            "required": [
              "id",
              "type",
              "productId",
              "quantity",
              "expiresAt"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "type": {
                "const": "add_to_cart",
                "type": "string"
              },
              "productId": {
                "type": "string"
              },
              "quantity": {
                "type": "integer"
              },
              "expiresAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "orders": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderCard"
            }
          }
        },
        "additionalProperties": true
      },
      "Thread": {
        "type": "object",
        "description": "Thread as serialized for clients: server-only fields (`state`, `accessToken`, `owner`,\n`restoredFromClientHistory`) are stripped.\n",
        "required": [
          "id",
          "subject",
          "channel",
          "meta",
          "provider",
          "createdAt",
          "updatedAt",
          "messages"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "`thr_<uuid>`"
          },
          "subject": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "channel": {
            "type": "string",
            "description": "Defaults to `chat` on the API routes."
          },
          "meta": {
            "type": "object",
            "additionalProperties": true
          },
          "provider": {
            "$ref": "#/components/schemas/Provider"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          }
        }
      },
      "RecoveryMessage": {
        "type": "object",
        "description": "Client-held history used only to rebuild a thread the store no longer has. Reduced to\nplain user/ai text (max 20 messages, 2000 chars each); every other field is dropped.\n",
        "required": [
          "sender",
          "text"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "sender": {
            "type": "string",
            "enum": [
              "user",
              "ai"
            ]
          },
          "text": {
            "type": "string"
          },
          "timestamp": {
            "type": "string"
          }
        }
      },
      "ChatMeta": {
        "type": "object",
        "description": "Free-form request metadata forwarded to the agent (department, custom fields, page\ncontext, `sessionId`, and `agent`/`agentConfig`/`kb` identity hints). Stored on the\nthread. Shape is merchant-defined.\n",
        "properties": {
          "sessionId": {
            "type": "string"
          },
          "threadMessages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RecoveryMessage"
            }
          }
        },
        "additionalProperties": true
      },
      "ChatRequestBase": {
        "type": "object",
        "properties": {
          "clientMessageId": {
            "type": "string",
            "maxLength": 256,
            "description": "Stable identity for one intentional send; preserve it across retries."
          },
          "message": {
            "type": "string",
            "description": "Visitor text. Required unless `attachments` is non-empty."
          },
          "stream": {
            "type": "boolean",
            "description": "`true` answers `text/event-stream`."
          },
          "provider": {
            "$ref": "#/components/schemas/Provider"
          },
          "channel": {
            "type": "string",
            "default": "chat"
          },
          "meta": {
            "$ref": "#/components/schemas/ChatMeta"
          },
          "attachments": {
            "type": "array",
            "maxItems": 5,
            "items": {
              "$ref": "#/components/schemas/Attachment"
            }
          },
          "sessionId": {
            "type": "string",
            "description": "Widget session id; stored on the thread and copied into `meta.sessionId`."
          },
          "threadMessages": {
            "type": "array",
            "description": "Recovery history when the referenced thread is gone (see `RecoveryMessage`).",
            "items": {
              "$ref": "#/components/schemas/RecoveryMessage"
            }
          },
          "threadToken": {
            "type": "string",
            "description": "Alternative to the `x-widget-thread-token` header."
          },
          "orgId": {
            "type": "string"
          },
          "agentId": {
            "type": "string"
          },
          "agent": {
            "type": "object",
            "description": "`{ orgId, agentId }` identity hint; must match the widget token when present.",
            "properties": {
              "orgId": {
                "type": "string"
              },
              "agentId": {
                "type": "string"
              }
            }
          }
        },
        "additionalProperties": true
      },
      "ThreadMessageRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ChatRequestBase"
          }
        ],
        "description": "Same as `ChatRequest` without `subject`/`threadId` (the id is in the path). `message` or `attachments` is required."
      },
      "ThreadCreateRequest": {
        "type": "object",
        "properties": {
          "subject": {
            "type": "string",
            "default": "New conversation"
          },
          "message": {
            "type": "string",
            "description": "Optional first visitor message."
          },
          "channel": {
            "type": "string",
            "default": "chat"
          },
          "meta": {
            "$ref": "#/components/schemas/ChatMeta"
          },
          "provider": {
            "$ref": "#/components/schemas/Provider"
          },
          "attachments": {
            "type": "array",
            "maxItems": 5,
            "items": {
              "$ref": "#/components/schemas/Attachment"
            }
          }
        }
      },
      "ThreadUpdateRequest": {
        "type": "object",
        "description": "Only these fields are applied; strings are truncated to 500 characters.",
        "properties": {
          "subject": {
            "type": "string",
            "maxLength": 500
          },
          "title": {
            "type": "string",
            "maxLength": 500
          },
          "meta": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "SSEStream": {
        "type": "string",
        "description": "Server-sent events. Each event is one `data: <json>` line followed by a blank line;\nthe stream ends with the literal `data: [DONE]`. Event payloads are `SSEEvent`.\nOrder: an optional `thread` event first (new/restored threads), optional `citation`\nevents, then `text_delta` chunks, an optional rich-content event, optional\n`escalated`/`outcome`/`redirect` events, a `message_id` event once the reply is complete\nand stored, then `[DONE]`. On failure an `error` event is written and the stream ends\nwithout `[DONE]`.\n",
        "examples": [
          "data: {\"type\":\"thread\",\"threadId\":\"thr_9c6e...\",\"threadToken\":\"WkY3...\"}\n\ndata: {\"type\":\"text_delta\",\"delta\":{\"text\":\"Hi there\"}}\n\ndata: {\"type\":\"text_delta\",\"delta\":{\"text\":\", how can I help?\"}}\n\ndata: {\"type\":\"product_cards\",\"content\":{\"products\":[{\"id\":\"p1\",\"title\":\"Serum\"}]}}\n\ndata: {\"type\":\"message_id\",\"messageId\":\"msg_mfu3x2k1_9c1e4a7b\"}\n\ndata: [DONE]\n"
        ]
      },
      "FeedbackRequest": {
        "type": "object",
        "required": [
          "threadId",
          "messageId",
          "reaction"
        ],
        "properties": {
          "threadId": {
            "type": "string"
          },
          "messageId": {
            "type": "string"
          },
          "reaction": {
            "$ref": "#/components/schemas/MessageReaction"
          },
          "comment": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500,
            "description": "Only kept with a non-null reaction."
          },
          "threadToken": {
            "type": "string",
            "description": "Alternative to the `x-widget-thread-token` header."
          }
        }
      },
      "Product": {
        "type": "object",
        "description": "Catalog product. Static-catalog items are normalized by the server (fields below);\nShopify results follow the same core fields but the exact set depends on the Shopify\nmapping, so extra properties are allowed.\n",
        "required": [
          "id",
          "title"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "descriptionHtml": {
            "type": "string"
          },
          "whyItMatches": {
            "type": "string"
          },
          "rating": {
            "type": [
              "number",
              "null"
            ]
          },
          "reviewCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "price": {
            "type": "string"
          },
          "image": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "handle": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": true
      },
      "CartAddRequest": {
        "type": "object",
        "required": [
          "variantId"
        ],
        "properties": {
          "variantId": {
            "type": "string",
            "description": "Shopify variant id (numeric or GID)."
          },
          "quantity": {
            "type": "integer",
            "minimum": 1,
            "default": 1
          },
          "sessionId": {
            "type": "string",
            "description": "Widget session id used to reuse the visitor's cart."
          },
          "discountCode": {
            "type": "string",
            "description": "Applied only when the server issued it for `threadId` (see route description)."
          },
          "threadId": {
            "type": "string"
          },
          "threadToken": {
            "type": "string"
          }
        }
      },
      "CartAddError": {
        "type": "object",
        "required": [
          "ok",
          "success",
          "variantId",
          "quantity",
          "error"
        ],
        "properties": {
          "ok": {
            "const": false,
            "type": "boolean"
          },
          "success": {
            "const": false,
            "type": "boolean"
          },
          "variantId": {
            "type": [
              "string",
              "null"
            ]
          },
          "quantity": {
            "type": "integer"
          },
          "error": {
            "type": "string",
            "description": "`shopify_not_configured`, `variant_id_required`, `cart_create_failed`, or the first Shopify `userErrors` message."
          },
          "discountIgnored": {
            "const": true,
            "type": "boolean"
          }
        }
      },
      "CartAttributionReceipt": {
        "type": "object",
        "required": [
          "conversionId",
          "sessionId",
          "action"
        ],
        "properties": {
          "conversionId": {
            "type": "string",
            "maxLength": 160
          },
          "sessionId": {
            "type": "string",
            "maxLength": 160
          },
          "action": {
            "type": "string",
            "enum": [
              "add_to_cart",
              "checkout",
              "view"
            ]
          },
          "threadId": {
            "type": "string",
            "maxLength": 160
          },
          "cartToken": {
            "type": "string",
            "maxLength": 255
          },
          "productId": {
            "type": "string",
            "maxLength": 255
          },
          "productTitle": {
            "type": "string",
            "maxLength": 255
          },
          "variantId": {
            "type": "string",
            "maxLength": 255
          },
          "source": {
            "type": "string",
            "maxLength": 80,
            "default": "response-chat-widget"
          },
          "landingPage": {
            "type": "string",
            "maxLength": 500
          },
          "referrer": {
            "type": "string",
            "maxLength": 500
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time"
          },
          "orgId": {
            "type": "string",
            "description": "Used only when the widget token carries no org id."
          },
          "agentId": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "WidgetAnalyticsEvent": {
        "type": "object",
        "required": [
          "action",
          "sessionId",
          "idempotencyKey"
        ],
        "properties": {
          "action": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9_.:-]{0,99}$",
            "description": "Event name (case-insensitive pattern match)."
          },
          "sessionId": {
            "type": "string",
            "maxLength": 256,
            "description": "Also accepted as `session_id`."
          },
          "idempotencyKey": {
            "type": "string",
            "maxLength": 512,
            "description": "Also accepted as `eventId`; the stored event id is derived from it."
          },
          "eventId": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "threadId": {
            "type": "string"
          },
          "pageUrl": {
            "type": "string"
          },
          "referrer": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "description": "Stored as `metadata` after credential-looking keys are stripped.",
            "additionalProperties": true
          },
          "orgId": {
            "type": "string"
          },
          "agentId": {
            "type": "string"
          },
          "token": {
            "type": "string",
            "description": "Widget token for `sendBeacon` callers that cannot set headers."
          }
        },
        "additionalProperties": true
      },
      "TelemetrySnapshot": {
        "type": "object",
        "description": "Process-local counters for one org/agent. `last` (most recent request summary) is present when requests were seen; its shape is not fixed here.",
        "required": [
          "orgId",
          "agentId",
          "requests",
          "errors",
          "kbHits",
          "kbMisses",
          "avgLatencyMs",
          "lastLatencyMs",
          "lastRequestAt",
          "tokens"
        ],
        "properties": {
          "orgId": {
            "type": [
              "string",
              "null"
            ]
          },
          "agentId": {
            "type": [
              "string",
              "null"
            ]
          },
          "requests": {
            "type": "integer"
          },
          "errors": {
            "type": "integer"
          },
          "kbHits": {
            "type": "integer"
          },
          "kbMisses": {
            "type": "integer"
          },
          "avgLatencyMs": {
            "type": "number"
          },
          "lastLatencyMs": {
            "type": "number"
          },
          "lastRequestAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "tokens": {
            "type": "object",
            "properties": {
              "prompt": {
                "type": "integer"
              },
              "completion": {
                "type": "integer"
              },
              "total": {
                "type": "integer"
              },
              "estimated": {
                "type": "integer"
              },
              "samples": {
                "type": "integer"
              }
            }
          },
          "last": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "ResponseReviewReport": {
        "type": "object",
        "required": [
          "ok",
          "generatedAt",
          "orgId",
          "agentId",
          "limit",
          "analysis",
          "nextSteps"
        ],
        "properties": {
          "ok": {
            "const": true,
            "type": "boolean"
          },
          "generatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "orgId": {
            "type": [
              "string",
              "null"
            ]
          },
          "agentId": {
            "type": [
              "string",
              "null"
            ]
          },
          "limit": {
            "type": "integer"
          },
          "analysis": {
            "type": "object",
            "description": "Output of `analyzeResponseRecords` (src/server/responseReview.js): includes\n`suggestions` (with `ruleDraft` entries), `workingExamples`, `regressionCases`\nand, with `includeRecords=true`, `records`. Exact shape is not fixed here.\n",
            "additionalProperties": true
          },
          "nextSteps": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ThreadAnalyticsReport": {
        "type": "object",
        "required": [
          "ok",
          "generatedAt",
          "orgId",
          "agentId",
          "limit",
          "range",
          "since",
          "possiblyTruncated",
          "analysis",
          "nextSteps"
        ],
        "properties": {
          "ok": {
            "const": true,
            "type": "boolean"
          },
          "generatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "orgId": {
            "type": [
              "string",
              "null"
            ]
          },
          "agentId": {
            "type": [
              "string",
              "null"
            ]
          },
          "limit": {
            "type": "integer"
          },
          "range": {
            "type": [
              "string",
              "null"
            ]
          },
          "since": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "possiblyTruncated": {
            "type": "boolean",
            "description": "`true` when the record count hit `limit`."
          },
          "analysis": {
            "type": "object",
            "description": "Output of `analyzeThreadResponseAnalytics` (src/server/threadAnalytics.js):\nincludes `singleResponseReasons`, `multiResponseReasons`, `daily`,\n`pairCountDistribution` and, with `includeThreads=true`, sampled threads. Exact\nshape is not fixed here.\n",
            "additionalProperties": true
          },
          "nextSteps": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "PublishedWidgetSettings": {
        "type": "object",
        "description": "Published merchant configuration (the `ChatWidgetSettings` shape in src/types/chat.ts,\nas stored by the admin API). Keys are merchant-defined; see WIDGET-CONFIGURATION.md\n\"Remote Settings\".\n",
        "additionalProperties": true
      },
      "GorgiasWebhookPayload": {
        "type": "object",
        "description": "Gorgias HTTP integration payload. Either a ticket event (`ticket_id` or `ticket.id`\nwith `messages[]`), a single `message`, or a flat message (`ticket_id` + `body_text`).\n",
        "properties": {
          "ticket_id": {
            "type": [
              "string",
              "integer"
            ]
          },
          "ticket": {
            "type": "object",
            "properties": {
              "id": {
                "type": [
                  "string",
                  "integer"
                ]
              }
            },
            "additionalProperties": true
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GorgiasMessage"
            }
          },
          "message": {
            "$ref": "#/components/schemas/GorgiasMessage"
          },
          "body_text": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "GorgiasMessage": {
        "type": "object",
        "properties": {
          "from_agent": {
            "type": "boolean"
          },
          "public": {
            "type": "boolean"
          },
          "body_text": {
            "type": "string"
          },
          "body_html": {
            "type": "string"
          },
          "source": {
            "type": "object",
            "properties": {
              "extra": {
                "type": "object",
                "properties": {
                  "suppress_in_chat": {
                    "type": "boolean"
                  },
                  "kind": {
                    "type": "string"
                  }
                },
                "additionalProperties": true
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "LiveChatState": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ai",
              "active",
              "resolved"
            ]
          },
          "operatorId": {
            "type": "string"
          },
          "operatorName": {
            "type": "string"
          },
          "leaseExpiresAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LiveChatSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "customer": {
            "type": "string"
          },
          "messageCount": {
            "type": "integer"
          },
          "preview": {
            "type": "string"
          },
          "liveChat": {
            "$ref": "#/components/schemas/LiveChatState"
          },
          "canReply": {
            "type": "boolean"
          },
          "noteCount": {
            "type": "integer"
          },
          "lastCustomerMessageId": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastCustomerMessageAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "awaitingReply": {
            "type": "boolean"
          },
          "waitingSince": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "OperatorMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "sender": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "authorName": {
            "type": "string"
          },
          "operatorEvent": {
            "type": "string",
            "enum": [
              "joined",
              "released",
              "resolved"
            ]
          },
          "attachments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "OperatorNote": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "authorName": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LiveChatAcknowledgement": {
        "type": "object",
        "required": [
          "threadId",
          "reply",
          "escalated",
          "liveChat"
        ],
        "properties": {
          "threadId": {
            "type": "string"
          },
          "reply": {
            "const": "",
            "type": "string"
          },
          "escalated": {
            "const": false,
            "type": "boolean"
          },
          "liveChat": {
            "type": "object",
            "properties": {
              "accepted": {
                "const": true,
                "type": "boolean"
              },
              "status": {
                "const": "active",
                "type": "string"
              },
              "operatorName": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "widgetToken": []
    },
    {
      "widgetBearer": []
    }
  ],
  "paths": {
    "/api/widget/session": {
      "post": {
        "tags": [
          "Settings"
        ],
        "summary": "Issue a short-lived token for the deployment-bound storefront",
        "description": "Opt-in with WIDGET_SESSION_ENABLED=true and WIDGET_LIVE_CHAT_ENABLED=true.\nRequires an exact Origin from WIDGET_SESSION_ALLOWED_ORIGINS. Organization,\nagent and optional site claims come from server configuration; request fields\ncannot choose them. This identifies the public widget deployment, not the\nshopper. Thread capability tokens still protect individual conversations.\nThe embed loader can renew this token using data-rw-session-endpoint.\n",
        "security": [],
        "parameters": [
          {
            "in": "header",
            "name": "Origin",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Non-cacheable signed widget token and renewal interval",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "token",
                    "expiresAt",
                    "refreshAfterMs"
                  ],
                  "properties": {
                    "token": {
                      "type": "string"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "refreshAfterMs": {
                      "type": "integer",
                      "minimum": 48000,
                      "maximum": 720000
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing or unapproved Origin"
          },
          "429": {
            "description": "Session issuance rate limit exceeded"
          },
          "503": {
            "description": "Session issuance is disabled or its bindings are incomplete"
          }
        }
      }
    },
    "/api/chat": {
      "post": {
        "tags": [
          "Chat"
        ],
        "operationId": "sendChatMessage",
        "summary": "Send a chat message (creates the thread when `threadId` is absent)",
        "description": "Appends the visitor message to the thread (creating one when `threadId` is missing or\nunknown, restoring it from `threadMessages` when supplied) and answers with the\nassistant reply. With `stream: true` the response is `text/event-stream`; otherwise JSON.\nA freshly created thread discloses its capability token once (`threadToken` in JSON or\nthe leading SSE `thread` event). Attachments are validated first (max 5, 10 MB each,\nimage/PDF/text/CSV; `400 invalid_attachment` / `413 payload_too_large`). A post with\nneither `message` text nor `attachments` answers `400 { error: 'message required' }`\n(as `POST /api/threads/{id}/messages` does) before any thread is created, restored or\ngiven a token. An existing thread keeps its stored `provider`; a body `provider` only\nselects the provider for this reply.\n Native human takeover may return a JSON liveChat acknowledgement even with stream=true. With clientMessageId and session identity, complete replies replay for ten minutes with X-Idempotent-Replay: true. 409 message_outcome_unknown requires conversation refresh and reconciliation, not automatic execution. A complete SSE replay requires a terminated [DONE] data event and no error events. Reporting defaults to fail-open; RESPONSE_OUTBOX_STRICT=true makes a configured reporting enqueue failure return 503 (or an SSE error without [DONE]).\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules. Captured responses larger than 256 KiB are not replayable; a later retry reports an unknown outcome rather than re-executing.",
        "security": [
          {
            "widgetToken": []
          },
          {
            "widgetBearer": []
          },
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Assistant reply. `application/json` when `stream` is falsy; `text/event-stream` when\n`stream: true` (see `SSEStream`).\n",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ChatResponse"
                    },
                    {
                      "$ref": "#/components/schemas/LiveChatAcknowledgement"
                    }
                  ]
                }
              },
              "text/event-stream": {
                "schema": {
                  "$ref": "#/components/schemas/SSEStream"
                }
              }
            }
          },
          "400": {
            "description": "No message or attachments, attachments rejected, or malformed JSON",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "messageRequired": {
                    "value": {
                      "error": "message required"
                    }
                  },
                  "invalidAttachment": {
                    "value": {
                      "error": "invalid_attachment"
                    }
                  },
                  "badJson": {
                    "value": {
                      "error": "bad_request",
                      "requestId": "3f1c..."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "error": "thread_forbidden"
                    }
                  },
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "idempotency_conflict, message_in_progress, message_outcome_unknown, or thread_busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over the JSON limit, or an attachment over 10 MB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "body": {
                    "value": {
                      "error": "payload_too_large",
                      "requestId": "3f1c..."
                    }
                  },
                  "attachment": {
                    "value": {
                      "error": "payload_too_large"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Provider failure. The chat handler answers `{ error: <provider error message>, type }`\n(a human-readable message, not a code) or `{ error: '<PROVIDER>_API_KEY is not set.' }`\nwhen the selected provider has no key. The request and the provider key are validated\nbefore a thread is restored or created; when a later failure happens on a thread this\nrequest created or restored, the body also carries `threadId` and the one-time\n`threadToken`. Errors thrown before the handler answer `{ error: internal_error,\nrequestId }`. On an SSE stream the failure is emitted as an `error` event instead and\nthe stream ends.\n",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "required": [
                        "error"
                      ],
                      "properties": {
                        "error": {
                          "type": "string",
                          "description": "Provider error message."
                        },
                        "type": {
                          "type": "string",
                          "description": "Provider error type; defaults to `api_error`."
                        },
                        "threadId": {
                          "type": "string",
                          "description": "Present when this request created or restored the thread."
                        },
                        "threadToken": {
                          "type": "string",
                          "description": "The thread's capability token, disclosed with `threadId`."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "503": {
            "description": "Strict response-reporting queue unavailable/full, execution store or persistence failure. The default reporting mode logs/counts enqueue loss while permitting the reply. An interrupted stream can emit an error without [DONE].",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": []
      }
    },
    "/api/response/stateset": {
      "post": {
        "tags": [
          "Chat"
        ],
        "operationId": "sendChatMessageStateset",
        "summary": "Alias of `POST /api/chat`",
        "x-alias-of": "/api/chat",
        "security": [
          {
            "widgetToken": []
          },
          {
            "widgetBearer": []
          },
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Assistant reply. `application/json` when `stream` is falsy; `text/event-stream` when\n`stream: true` (see `SSEStream`).\n",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ChatResponse"
                    },
                    {
                      "$ref": "#/components/schemas/LiveChatAcknowledgement"
                    }
                  ]
                }
              },
              "text/event-stream": {
                "schema": {
                  "$ref": "#/components/schemas/SSEStream"
                }
              }
            }
          },
          "400": {
            "description": "No message or attachments, attachments rejected, or malformed JSON",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "messageRequired": {
                    "value": {
                      "error": "message required"
                    }
                  },
                  "invalidAttachment": {
                    "value": {
                      "error": "invalid_attachment"
                    }
                  },
                  "badJson": {
                    "value": {
                      "error": "bad_request",
                      "requestId": "3f1c..."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "error": "thread_forbidden"
                    }
                  },
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "idempotency_conflict, message_in_progress, message_outcome_unknown, or thread_busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over the JSON limit, or an attachment over 10 MB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "body": {
                    "value": {
                      "error": "payload_too_large",
                      "requestId": "3f1c..."
                    }
                  },
                  "attachment": {
                    "value": {
                      "error": "payload_too_large"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Provider failure. The chat handler answers `{ error: <provider error message>, type }`\n(a human-readable message, not a code) or `{ error: '<PROVIDER>_API_KEY is not set.' }`\nwhen the selected provider has no key. The request and the provider key are validated\nbefore a thread is restored or created; when a later failure happens on a thread this\nrequest created or restored, the body also carries `threadId` and the one-time\n`threadToken`. Errors thrown before the handler answer `{ error: internal_error,\nrequestId }`. On an SSE stream the failure is emitted as an `error` event instead and\nthe stream ends.\n",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "required": [
                        "error"
                      ],
                      "properties": {
                        "error": {
                          "type": "string",
                          "description": "Provider error message."
                        },
                        "type": {
                          "type": "string",
                          "description": "Provider error type; defaults to `api_error`."
                        },
                        "threadId": {
                          "type": "string",
                          "description": "Present when this request created or restored the thread."
                        },
                        "threadToken": {
                          "type": "string",
                          "description": "The thread's capability token, disclosed with `threadId`."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "503": {
            "description": "Strict response-reporting queue unavailable/full, execution store or persistence failure. The default reporting mode logs/counts enqueue loss while permitting the reply. An interrupted stream can emit an error without [DONE].",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": [],
        "description": " Native human takeover may return a JSON liveChat acknowledgement even with stream=true. With clientMessageId and session identity, complete replies replay for ten minutes with X-Idempotent-Replay: true. 409 message_outcome_unknown requires conversation refresh and reconciliation, not automatic execution. A complete SSE replay requires a terminated [DONE] data event and no error events. Reporting defaults to fail-open; RESPONSE_OUTBOX_STRICT=true makes a configured reporting enqueue failure return 503 (or an SSE error without [DONE]).\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules. Captured responses larger than 256 KiB are not replayable; a later retry reports an unknown outcome rather than re-executing."
      }
    },
    "/api/threads": {
      "post": {
        "tags": [
          "Threads"
        ],
        "operationId": "createThread",
        "summary": "Create a thread and receive its capability token",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThreadCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Thread created; `threadToken` is disclosed only here.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "threadId",
                    "threadToken",
                    "thread"
                  ],
                  "properties": {
                    "threadId": {
                      "type": "string"
                    },
                    "threadToken": {
                      "type": "string",
                      "description": "32-character base64url capability; echo it as `x-widget-thread-token`."
                    },
                    "thread": {
                      "$ref": "#/components/schemas/Thread"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "parameters": [],
        "description": "\n\nWidget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      }
    },
    "/api/threads/{id}": {
      "get": {
        "tags": [
          "Threads"
        ],
        "operationId": "getThread",
        "summary": "Load a thread and its messages",
        "security": [
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Thread and its messages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "thread",
                    "messages"
                  ],
                  "properties": {
                    "thread": {
                      "$ref": "#/components/schemas/Thread"
                    },
                    "messages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Message"
                      }
                    },
                    "escalated": {
                      "const": true,
                      "description": "Present when a Gorgias ticket exists for the thread.",
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "error": "thread_forbidden"
                    }
                  },
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown thread (or, for feedback, a message that is not an assistant message)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Thread id (`thr_<uuid>`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "x-widget-thread-token",
            "schema": {
              "type": "string"
            },
            "description": "Capability required for an existing thread that has an issued token. Legacy capability-less threads are denied when STATESET_THREAD_TOKEN_REQUIRED=true."
          }
        ],
        "description": "\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      },
      "patch": {
        "tags": [
          "Threads"
        ],
        "operationId": "updateThread",
        "summary": "Update a thread's subject, title or meta",
        "security": [
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThreadUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated thread",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "thread"
                  ],
                  "properties": {
                    "thread": {
                      "$ref": "#/components/schemas/Thread"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "error": "thread_forbidden"
                    }
                  },
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown thread (or, for feedback, a message that is not an assistant message)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Thread id (`thr_<uuid>`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "x-widget-thread-token",
            "schema": {
              "type": "string"
            },
            "description": "Capability required for an existing thread that has an issued token. Legacy capability-less threads are denied when STATESET_THREAD_TOKEN_REQUIRED=true."
          }
        ],
        "description": "\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      },
      "delete": {
        "tags": [
          "Threads"
        ],
        "operationId": "deleteThread",
        "summary": "Delete a thread",
        "description": "Answers `{ ok: false }` (200) when the thread does not exist.\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules.",
        "security": [
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Deletion outcome (`ok: false` when the id was unknown)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "error": "thread_forbidden"
                    }
                  },
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Thread id (`thr_<uuid>`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "x-widget-thread-token",
            "schema": {
              "type": "string"
            },
            "description": "Capability required for an existing thread that has an issued token. Legacy capability-less threads are denied when STATESET_THREAD_TOKEN_REQUIRED=true."
          }
        ]
      }
    },
    "/api/threads/{id}/messages": {
      "post": {
        "tags": [
          "Threads"
        ],
        "operationId": "postThreadMessage",
        "summary": "Append a message to a thread and receive the assistant reply",
        "description": "Same reply contract as `POST /api/chat` (JSON or SSE). When the thread is no longer in\nthe store it is rebuilt from `threadMessages` (or `meta.threadMessages`) and a new\ncapability token is disclosed; without recovery history the route answers 404. As on\n`POST /api/chat`, the thread keeps its stored `provider` (it is only filled when empty;\na body `provider` selects the provider for this reply) and customer identity in `meta`\n(email, phone, customer id, name) is recorded on the thread before the reply is built.\n Native human takeover may return a JSON liveChat acknowledgement even with stream=true. With clientMessageId and session identity, complete replies replay for ten minutes with X-Idempotent-Replay: true. 409 message_outcome_unknown requires conversation refresh and reconciliation, not automatic execution. A complete SSE replay requires a terminated [DONE] data event and no error events. Reporting defaults to fail-open; RESPONSE_OUTBOX_STRICT=true makes a configured reporting enqueue failure return 503 (or an SSE error without [DONE]).\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules. Captured responses larger than 256 KiB are not replayable; a later retry reports an unknown outcome rather than re-executing.",
        "security": [
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThreadMessageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Assistant reply. `application/json` when `stream` is falsy; `text/event-stream` when\n`stream: true` (see `SSEStream`).\n",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ChatResponse"
                    },
                    {
                      "$ref": "#/components/schemas/LiveChatAcknowledgement"
                    }
                  ]
                }
              },
              "text/event-stream": {
                "schema": {
                  "$ref": "#/components/schemas/SSEStream"
                }
              }
            }
          },
          "400": {
            "description": "No message or attachments, attachments rejected, or malformed JSON",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "messageRequired": {
                    "value": {
                      "error": "message required"
                    }
                  },
                  "invalidAttachment": {
                    "value": {
                      "error": "invalid_attachment"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "error": "thread_forbidden"
                    }
                  },
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown thread (or, for feedback, a message that is not an assistant message)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found"
                }
              }
            }
          },
          "409": {
            "description": "idempotency_conflict, message_in_progress, message_outcome_unknown, or thread_busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over the JSON limit, or an attachment over 10 MB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "body": {
                    "value": {
                      "error": "payload_too_large",
                      "requestId": "3f1c..."
                    }
                  },
                  "attachment": {
                    "value": {
                      "error": "payload_too_large"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Provider failure. The chat handler answers `{ error: <provider error message>, type }`\n(a human-readable message, not a code) or `{ error: '<PROVIDER>_API_KEY is not set.' }`\nwhen the selected provider has no key. The request and the provider key are validated\nbefore a thread is restored or created; when a later failure happens on a thread this\nrequest created or restored, the body also carries `threadId` and the one-time\n`threadToken`. Errors thrown before the handler answer `{ error: internal_error,\nrequestId }`. On an SSE stream the failure is emitted as an `error` event instead and\nthe stream ends.\n",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "required": [
                        "error"
                      ],
                      "properties": {
                        "error": {
                          "type": "string",
                          "description": "Provider error message."
                        },
                        "type": {
                          "type": "string",
                          "description": "Provider error type; defaults to `api_error`."
                        },
                        "threadId": {
                          "type": "string",
                          "description": "Present when this request created or restored the thread."
                        },
                        "threadToken": {
                          "type": "string",
                          "description": "The thread's capability token, disclosed with `threadId`."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Strict response-reporting queue unavailable/full, execution store or persistence failure. The default reporting mode logs/counts enqueue loss while permitting the reply. An interrupted stream can emit an error without [DONE]."
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Thread id (`thr_<uuid>`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "x-widget-thread-token",
            "schema": {
              "type": "string"
            },
            "description": "Capability required for an existing thread that has an issued token. Legacy capability-less threads are denied when STATESET_THREAD_TOKEN_REQUIRED=true."
          }
        ]
      }
    },
    "/api/response/threads": {
      "post": {
        "tags": [
          "Threads"
        ],
        "operationId": "createThreadResponse",
        "summary": "Alias of `POST /api/threads`",
        "x-alias-of": "/api/threads",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThreadCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Thread created; `threadToken` is disclosed only here.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "threadId",
                    "threadToken",
                    "thread"
                  ],
                  "properties": {
                    "threadId": {
                      "type": "string"
                    },
                    "threadToken": {
                      "type": "string",
                      "description": "32-character base64url capability; echo it as `x-widget-thread-token`."
                    },
                    "thread": {
                      "$ref": "#/components/schemas/Thread"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "parameters": [],
        "description": "\n\nWidget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      }
    },
    "/api/response/threads/{id}": {
      "get": {
        "tags": [
          "Threads"
        ],
        "operationId": "getThreadResponse",
        "summary": "Alias of `GET /api/threads/{id}`",
        "x-alias-of": "/api/threads/{id}",
        "security": [
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Thread and its messages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "thread",
                    "messages"
                  ],
                  "properties": {
                    "thread": {
                      "$ref": "#/components/schemas/Thread"
                    },
                    "messages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Message"
                      }
                    },
                    "escalated": {
                      "const": true,
                      "description": "Present when a Gorgias ticket exists for the thread.",
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "error": "thread_forbidden"
                    }
                  },
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown thread (or, for feedback, a message that is not an assistant message)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Thread id (`thr_<uuid>`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "x-widget-thread-token",
            "schema": {
              "type": "string"
            },
            "description": "Capability required for an existing thread that has an issued token. Legacy capability-less threads are denied when STATESET_THREAD_TOKEN_REQUIRED=true."
          }
        ],
        "description": "\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      },
      "patch": {
        "tags": [
          "Threads"
        ],
        "operationId": "updateThreadResponse",
        "summary": "Alias of `PATCH /api/threads/{id}`",
        "x-alias-of": "/api/threads/{id}",
        "security": [
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThreadUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated thread",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "thread"
                  ],
                  "properties": {
                    "thread": {
                      "$ref": "#/components/schemas/Thread"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "error": "thread_forbidden"
                    }
                  },
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown thread (or, for feedback, a message that is not an assistant message)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Thread id (`thr_<uuid>`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "x-widget-thread-token",
            "schema": {
              "type": "string"
            },
            "description": "Capability required for an existing thread that has an issued token. Legacy capability-less threads are denied when STATESET_THREAD_TOKEN_REQUIRED=true."
          }
        ],
        "description": "\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      },
      "delete": {
        "tags": [
          "Threads"
        ],
        "operationId": "deleteThreadResponse",
        "summary": "Alias of `DELETE /api/threads/{id}`",
        "x-alias-of": "/api/threads/{id}",
        "security": [
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "Deletion outcome (`ok: false` when the id was unknown)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "error": "thread_forbidden"
                    }
                  },
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Thread id (`thr_<uuid>`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "x-widget-thread-token",
            "schema": {
              "type": "string"
            },
            "description": "Capability required for an existing thread that has an issued token. Legacy capability-less threads are denied when STATESET_THREAD_TOKEN_REQUIRED=true."
          }
        ],
        "description": "\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      }
    },
    "/api/response/threads/{id}/messages": {
      "post": {
        "tags": [
          "Threads"
        ],
        "operationId": "postThreadMessageResponse",
        "summary": "Alias of `POST /api/threads/{id}/messages`",
        "x-alias-of": "/api/threads/{id}/messages",
        "security": [
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThreadMessageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Assistant reply. `application/json` when `stream` is falsy; `text/event-stream` when\n`stream: true` (see `SSEStream`).\n",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ChatResponse"
                    },
                    {
                      "$ref": "#/components/schemas/LiveChatAcknowledgement"
                    }
                  ]
                }
              },
              "text/event-stream": {
                "schema": {
                  "$ref": "#/components/schemas/SSEStream"
                }
              }
            }
          },
          "400": {
            "description": "No message or attachments, attachments rejected, or malformed JSON",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "messageRequired": {
                    "value": {
                      "error": "message required"
                    }
                  },
                  "invalidAttachment": {
                    "value": {
                      "error": "invalid_attachment"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "error": "thread_forbidden"
                    }
                  },
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown thread (or, for feedback, a message that is not an assistant message)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found"
                }
              }
            }
          },
          "409": {
            "description": "idempotency_conflict, message_in_progress, message_outcome_unknown, or thread_busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over the JSON limit, or an attachment over 10 MB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "body": {
                    "value": {
                      "error": "payload_too_large",
                      "requestId": "3f1c..."
                    }
                  },
                  "attachment": {
                    "value": {
                      "error": "payload_too_large"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Provider failure. The chat handler answers `{ error: <provider error message>, type }`\n(a human-readable message, not a code) or `{ error: '<PROVIDER>_API_KEY is not set.' }`\nwhen the selected provider has no key. The request and the provider key are validated\nbefore a thread is restored or created; when a later failure happens on a thread this\nrequest created or restored, the body also carries `threadId` and the one-time\n`threadToken`. Errors thrown before the handler answer `{ error: internal_error,\nrequestId }`. On an SSE stream the failure is emitted as an `error` event instead and\nthe stream ends.\n",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "required": [
                        "error"
                      ],
                      "properties": {
                        "error": {
                          "type": "string",
                          "description": "Provider error message."
                        },
                        "type": {
                          "type": "string",
                          "description": "Provider error type; defaults to `api_error`."
                        },
                        "threadId": {
                          "type": "string",
                          "description": "Present when this request created or restored the thread."
                        },
                        "threadToken": {
                          "type": "string",
                          "description": "The thread's capability token, disclosed with `threadId`."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Strict response-reporting queue unavailable/full, execution store or persistence failure. The default reporting mode logs/counts enqueue loss while permitting the reply. An interrupted stream can emit an error without [DONE]."
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Thread id (`thr_<uuid>`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "x-widget-thread-token",
            "schema": {
              "type": "string"
            },
            "description": "Capability required for an existing thread that has an issued token. Legacy capability-less threads are denied when STATESET_THREAD_TOKEN_REQUIRED=true."
          }
        ],
        "description": " Native human takeover may return a JSON liveChat acknowledgement even with stream=true. With clientMessageId and session identity, complete replies replay for ten minutes with X-Idempotent-Replay: true. 409 message_outcome_unknown requires conversation refresh and reconciliation, not automatic execution. A complete SSE replay requires a terminated [DONE] data event and no error events. Reporting defaults to fail-open; RESPONSE_OUTBOX_STRICT=true makes a configured reporting enqueue failure return 503 (or an SSE error without [DONE]).\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules. Captured responses larger than 256 KiB are not replayable; a later retry reports an unknown outcome rather than re-executing."
      }
    },
    "/api/response/feedback": {
      "post": {
        "tags": [
          "Feedback"
        ],
        "operationId": "submitMessageFeedback",
        "summary": "React to an assistant message (thumbs up/down, optional comment)",
        "description": "Only assistant (`sender: ai`) messages accept feedback; user messages and unknown ids\nanswer `404 not_found` so callers cannot tell them apart. `reaction: null` clears the\nreaction and drops the comment. The thread persist is awaited; the Hasura mirror is\nfire-and-forget.\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules.",
        "security": [
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedbackRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reaction stored",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "threadId",
                    "messageId",
                    "reaction"
                  ],
                  "properties": {
                    "ok": {
                      "const": true,
                      "type": "boolean"
                    },
                    "threadId": {
                      "type": "string"
                    },
                    "messageId": {
                      "type": "string"
                    },
                    "reaction": {
                      "$ref": "#/components/schemas/MessageReaction"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Body failed validation (missing ids, bad reaction, comment over 500 chars)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "invalid_feedback"
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "error": "thread_forbidden"
                    }
                  },
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown thread (or, for feedback, a message that is not an assistant message)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "parameters": [
          {
            "in": "header",
            "name": "x-widget-thread-token",
            "schema": {
              "type": "string"
            },
            "description": "Capability for the thread containing the stored AI message being rated."
          }
        ]
      }
    },
    "/api/feedback": {
      "post": {
        "tags": [
          "Feedback"
        ],
        "operationId": "submitMessageFeedbackAlias",
        "summary": "Alias of `POST /api/response/feedback`",
        "x-alias-of": "/api/response/feedback",
        "security": [
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedbackRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reaction stored",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "threadId",
                    "messageId",
                    "reaction"
                  ],
                  "properties": {
                    "ok": {
                      "const": true,
                      "type": "boolean"
                    },
                    "threadId": {
                      "type": "string"
                    },
                    "messageId": {
                      "type": "string"
                    },
                    "reaction": {
                      "$ref": "#/components/schemas/MessageReaction"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Body failed validation (missing ids, bad reaction, comment over 500 chars)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "invalid_feedback"
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Thread capability missing/wrong, thread owner mismatch, or token identity mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "thread": {
                    "value": {
                      "error": "thread_forbidden"
                    }
                  },
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown thread (or, for feedback, a message that is not an assistant message)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "parameters": [
          {
            "in": "header",
            "name": "x-widget-thread-token",
            "schema": {
              "type": "string"
            },
            "description": "Capability for the thread containing the stored AI message being rated."
          }
        ],
        "description": "\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      }
    },
    "/api/products": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "operationId": "listProducts",
        "summary": "List or search products",
        "description": "With `q` the catalog (Shopify when configured, else the static catalog) is searched;\nwithout it the Shopify product list or the static catalog is returned, enriched with\nJSON-LD metadata when `STATESET_PRODUCT_ENRICH` is on.\n\nWidget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search terms. Without it the full list is returned.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Products",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "products"
                  ],
                  "properties": {
                    "ok": {
                      "const": true,
                      "type": "boolean"
                    },
                    "products": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        }
      }
    },
    "/api/products/search": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "operationId": "searchProducts",
        "summary": "Alias of `GET /api/products`",
        "x-alias-of": "/api/products",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search terms. Without it the full list is returned.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Products",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "products"
                  ],
                  "properties": {
                    "ok": {
                      "const": true,
                      "type": "boolean"
                    },
                    "products": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "description": "\n\nWidget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      }
    },
    "/api/cart/add": {
      "post": {
        "tags": [
          "Cart"
        ],
        "operationId": "addToCart",
        "summary": "Add a variant to a Shopify cart",
        "description": "`discountCode` is applied only when the server issued it for this request's thread\n(the YSE first-purchase code on a thread whose eligibility check passed; the thread\ntoken is enforced like the thread routes). Any other code is dropped and\n`discountIgnored: true` is added to the response.\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules.",
        "security": [
          {
            "widgetToken": []
          },
          {
            "widgetBearer": []
          },
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CartAddRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Line added to the Shopify cart",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CartAddResponse"
                }
              }
            }
          },
          "400": {
            "description": "Shopify not configured, missing variant, or Shopify user error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CartAddError"
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "parameters": []
      }
    },
    "/api/shopify/cart/add": {
      "post": {
        "tags": [
          "Cart"
        ],
        "operationId": "addToShopifyCart",
        "summary": "Alias of `POST /api/cart/add`",
        "x-alias-of": "/api/cart/add",
        "security": [
          {
            "widgetToken": []
          },
          {
            "widgetBearer": []
          },
          {
            "widgetToken": [],
            "threadToken": []
          },
          {
            "widgetBearer": [],
            "threadToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CartAddRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Line added to the Shopify cart",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CartAddResponse"
                }
              }
            }
          },
          "400": {
            "description": "Shopify not configured, missing variant, or Shopify user error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CartAddError"
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "parameters": [],
        "description": "\n\nFor an existing protected thread, send its `x-widget-thread-token` capability in addition to widget identity. Widget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      }
    },
    "/api/cart-attribution": {
      "post": {
        "tags": [
          "Cart"
        ],
        "operationId": "recordCartAttribution",
        "summary": "Record a Shopify cart receipt attributed to a chat conversion",
        "description": "Writes a `cart_attribution_confirmed` widget analytics event keyed by\n`cart_attribution:<conversionId>:<action>:<variantId|productId|cartToken|cart>` so\nretries are idempotent (`duplicate: true`).\n\nWidget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CartAttributionReceipt"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Receipt stored (or already stored)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "duplicate",
                    "eventId"
                  ],
                  "properties": {
                    "ok": {
                      "const": true,
                      "type": "boolean"
                    },
                    "duplicate": {
                      "type": "boolean"
                    },
                    "eventId": {
                      "type": "string",
                      "description": "The idempotency key of the stored event."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing `conversionId`, `sessionId`, `action` or org id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "example": {
                  "ok": false,
                  "error": "invalid_receipt"
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          },
          "502": {
            "description": "Analytics store unavailable or rejected the write",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "examples": {
                  "notConfigured": {
                    "value": {
                      "ok": false,
                      "error": "missing_graphql_config"
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": []
      }
    },
    "/api/analytics": {
      "post": {
        "tags": [
          "Analytics"
        ],
        "operationId": "recordWidgetAnalyticsEvent",
        "summary": "Record a widget analytics event",
        "description": "Idempotent on `idempotencyKey` (or `eventId`) per org. `orgId`/`agentId` in the body\nmust match the verified widget identity when present. Bodies over 64 KB are rejected.\nCredential-looking keys (`token`, `apiKey`, `authorization`, ...) are stripped from\n`data` before storage. This route accepts the widget token as `token` in the body for\n`navigator.sendBeacon` callers and is rate limited in its own bucket.\n\nWidget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WidgetAnalyticsEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event stored (or already stored)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "eventId"
                  ],
                  "properties": {
                    "ok": {
                      "const": true,
                      "type": "boolean"
                    },
                    "eventId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Deterministic id derived from `orgId` and the idempotency key."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Event failed validation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "example": {
                  "ok": false,
                  "error": "invalid_analytics_event"
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Body identity disagrees with the verified widget identity, or token org/agent mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/OkError"
                    },
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ]
                },
                "examples": {
                  "identity": {
                    "value": {
                      "ok": false,
                      "error": "analytics_identity_mismatch"
                    }
                  },
                  "token": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          },
          "502": {
            "description": "Analytics store rejected the write",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "example": {
                  "ok": false,
                  "error": "analytics_save_failed"
                }
              }
            }
          },
          "503": {
            "description": "Analytics store not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "example": {
                  "ok": false,
                  "error": "analytics_unavailable"
                }
              }
            }
          }
        },
        "parameters": []
      }
    },
    "/api/telemetry": {
      "get": {
        "tags": [
          "Telemetry"
        ],
        "operationId": "getTelemetry",
        "summary": "In-memory request telemetry for an org/agent",
        "description": "Snapshot of the process-local telemetry counters. The org id comes only from the verified\nwidget token; a request org (`x-org-id`, `orgId`) is ignored, and without a token org the\nroute answers `401 unauthorized` (outside production, `STATESET_ORG_ID` is a logged\nfallback). Disabled deployments (`STATESET_TELEMETRY_ENABLED=false`)\nanswer `404 telemetry_disabled`. The `context` field (resolved agent configuration) is\nstripped before the snapshot is returned.\n\nWidget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules.",
        "parameters": [
          {
            "name": "orgId",
            "in": "query",
            "required": false,
            "deprecated": true,
            "description": "Ignored by the analytics routes (telemetry, response review, thread analytics): their org comes only from the verified widget token, and they answer `401 unauthorized` without one (outside production, `STATESET_ORG_ID` is a logged fallback). Kept for older clients.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agentId",
            "in": "query",
            "required": false,
            "description": "Agent id hint (also `agent_id`, or header `x-agent-id`). Overridden by the widget token when present; must match it.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Telemetry snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "generatedAt",
                    "telemetry"
                  ],
                  "properties": {
                    "ok": {
                      "const": true,
                      "type": "boolean"
                    },
                    "generatedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "telemetry": {
                      "$ref": "#/components/schemas/TelemetrySnapshot"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Telemetry disabled on this deployment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "telemetry_disabled"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        }
      }
    },
    "/api/response/telemetry": {
      "get": {
        "tags": [
          "Telemetry"
        ],
        "operationId": "getTelemetryResponse",
        "summary": "Alias of `GET /api/telemetry`",
        "x-alias-of": "/api/telemetry",
        "parameters": [
          {
            "name": "orgId",
            "in": "query",
            "required": false,
            "deprecated": true,
            "description": "Ignored by the analytics routes (telemetry, response review, thread analytics): their org comes only from the verified widget token, and they answer `401 unauthorized` without one (outside production, `STATESET_ORG_ID` is a logged fallback). Kept for older clients.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agentId",
            "in": "query",
            "required": false,
            "description": "Agent id hint (also `agent_id`, or header `x-agent-id`). Overridden by the widget token when present; must match it.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Telemetry snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "generatedAt",
                    "telemetry"
                  ],
                  "properties": {
                    "ok": {
                      "const": true,
                      "type": "boolean"
                    },
                    "generatedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "telemetry": {
                      "$ref": "#/components/schemas/TelemetrySnapshot"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Telemetry disabled on this deployment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "telemetry_disabled"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        },
        "description": "\n\nWidget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      }
    },
    "/api/response/review": {
      "get": {
        "tags": [
          "Review"
        ],
        "operationId": "getResponseReview",
        "summary": "Analyze recent stored responses and suggest rule/example drafts",
        "parameters": [
          {
            "name": "orgId",
            "in": "query",
            "required": false,
            "deprecated": true,
            "description": "Ignored by the analytics routes (telemetry, response review, thread analytics): their org comes only from the verified widget token, and they answer `401 unauthorized` without one (outside production, `STATESET_ORG_ID` is a logged fallback). Kept for older clients.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agentId",
            "in": "query",
            "required": false,
            "description": "Agent id hint (also `agent_id`, or header `x-agent-id`). Overridden by the widget token when present; must match it.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Records to analyze; default 100, max 500.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500
            }
          },
          {
            "name": "includeRecords",
            "in": "query",
            "required": false,
            "description": "`true` keeps the analyzed records in `analysis.records`.",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Review report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseReviewReport"
                }
              }
            }
          },
          "400": {
            "description": "Review could not run",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "examples": {
                  "missingConfig": {
                    "value": {
                      "ok": false,
                      "error": "missing_graphql_config"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          },
          "502": {
            "description": "Responses store query failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "example": {
                  "ok": false,
                  "error": "responses_query_failed"
                }
              }
            }
          }
        },
        "description": "\n\nWidget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      },
      "post": {
        "tags": [
          "Review"
        ],
        "operationId": "postResponseReview",
        "summary": "Same as `GET /api/response/review` with parameters in the body",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "orgId": {
                    "type": "string"
                  },
                  "agentId": {
                    "type": "string"
                  },
                  "limit": {
                    "type": "integer",
                    "description": "Records to analyze; default 100, max 500."
                  },
                  "includeRecords": {
                    "type": [
                      "boolean",
                      "string"
                    ],
                    "description": "Include the analyzed records in `analysis.records` (string `'true'` also accepted)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Review report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseReviewReport"
                }
              }
            }
          },
          "400": {
            "description": "Review could not run",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "examples": {
                  "missingConfig": {
                    "value": {
                      "ok": false,
                      "error": "missing_graphql_config"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          },
          "502": {
            "description": "Responses store query failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "example": {
                  "ok": false,
                  "error": "responses_query_failed"
                }
              }
            }
          }
        },
        "parameters": [],
        "description": "\n\nWidget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      }
    },
    "/api/response/thread-analytics": {
      "get": {
        "tags": [
          "Review"
        ],
        "operationId": "getThreadAnalytics",
        "summary": "Conversation-length analytics over stored responses",
        "parameters": [
          {
            "name": "orgId",
            "in": "query",
            "required": false,
            "deprecated": true,
            "description": "Ignored by the analytics routes (telemetry, response review, thread analytics): their org comes only from the verified widget token, and they answer `401 unauthorized` without one (outside production, `STATESET_ORG_ID` is a logged fallback). Kept for older clients.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agentId",
            "in": "query",
            "required": false,
            "description": "Agent id hint (also `agent_id`, or header `x-agent-id`). Overridden by the widget token when present; must match it.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Records to analyze; default 5000, max 10000.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000
            }
          },
          {
            "name": "includeThreads",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          },
          {
            "name": "sampleLimit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 5
            }
          },
          {
            "name": "range",
            "in": "query",
            "required": false,
            "description": "`<n>d` for the last n days (1-365) or `all`; omitted means no lower bound.",
            "schema": {
              "type": "string",
              "pattern": "^([1-9]\\d{0,2}d|all)$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Thread analytics report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThreadAnalyticsReport"
                }
              }
            }
          },
          "400": {
            "description": "Bad `range`, or analytics could not run",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "examples": {
                  "range": {
                    "value": {
                      "ok": false,
                      "error": "invalid_analytics_range"
                    }
                  },
                  "missingConfig": {
                    "value": {
                      "ok": false,
                      "error": "missing_graphql_config"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          },
          "502": {
            "description": "Responses store query failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "example": {
                  "ok": false,
                  "error": "response_thread_analytics_query_failed"
                }
              }
            }
          }
        },
        "description": "\n\nWidget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      },
      "post": {
        "tags": [
          "Review"
        ],
        "operationId": "postThreadAnalytics",
        "summary": "Same as `GET /api/response/thread-analytics` with parameters in the body",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "orgId": {
                    "type": "string"
                  },
                  "agentId": {
                    "type": "string"
                  },
                  "limit": {
                    "type": "integer",
                    "description": "Records to analyze; default 5000, max 10000."
                  },
                  "includeThreads": {
                    "type": [
                      "boolean",
                      "string"
                    ]
                  },
                  "sampleLimit": {
                    "type": "integer",
                    "default": 5
                  },
                  "range": {
                    "type": "string",
                    "description": "`<n>d` (1-365) or `all`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Thread analytics report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThreadAnalyticsReport"
                }
              }
            }
          },
          "400": {
            "description": "Bad `range`, or analytics could not run",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "examples": {
                  "range": {
                    "value": {
                      "ok": false,
                      "error": "invalid_analytics_range"
                    }
                  },
                  "missingConfig": {
                    "value": {
                      "ok": false,
                      "error": "missing_graphql_config"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Widget token missing, malformed, expired, from the future, or badly signed",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "authRequired": {
                    "value": {
                      "error": "auth_required"
                    }
                  },
                  "invalidToken": {
                    "value": {
                      "error": "invalid_token"
                    }
                  },
                  "tokenExpired": {
                    "value": {
                      "error": "token_expired"
                    }
                  },
                  "tokenInFuture": {
                    "value": {
                      "error": "token_in_future"
                    }
                  },
                  "invalidSignature": {
                    "value": {
                      "error": "invalid_signature"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Request org/agent disagrees with the widget token claims",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "org": {
                    "value": {
                      "error": "org_mismatch"
                    }
                  },
                  "agent": {
                    "value": {
                      "error": "agent_mismatch"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          },
          "502": {
            "description": "Responses store query failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "example": {
                  "ok": false,
                  "error": "response_thread_analytics_query_failed"
                }
              }
            }
          }
        },
        "parameters": [],
        "description": "\n\nWidget identity is verified when signing is configured; tokenless compatibility does not establish tenant ownership. See the authentication and recovery guide for credential and retry rules."
      }
    },
    "/api/widget/settings": {
      "get": {
        "tags": [
          "Settings"
        ],
        "operationId": "getPublicWidgetSettings",
        "summary": "Published widget configuration for a site or origin",
        "description": "Public (no widget token); rate limited. Resolves the published configuration by\n`siteId`, else by the request `Origin` hostname. Answers `204` with no body when the\nadmin database is not configured, when no configuration matches the origin, or when the\nlookup fails. Responses carry `Access-Control-Allow-Origin: *` and\n`Cache-Control: public, max-age=60`.\n An explicit siteId with no published configuration returns 404 not_found.",
        "security": [],
        "parameters": [
          {
            "name": "siteId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Published configuration",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "settings"
                  ],
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/PublishedWidgetSettings"
                    }
                  }
                }
              }
            }
          },
          "204": {
            "description": "No configuration available for this origin (or admin DB not configured)"
          },
          "404": {
            "description": "Unknown `siteId`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests for this IP (scoped by verified org/agent)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitedError"
                }
              }
            }
          }
        }
      }
    },
    "/api/webhooks/gorgias": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "receiveGorgiasWebhook",
        "summary": "Gorgias HTTP integration webhook (agent replies into the chat thread)",
        "description": "Authenticated by the `token` query parameter matching `GORGIAS_WEBHOOK_SECRET`. In\nproduction a missing secret answers `503 webhook_not_configured`; outside production the\ngate is skipped. Public agent messages (`from_agent: true`, `public !== false`) are\nappended to the earliest thread carrying the ticket id; acknowledgement messages and\nmessages flagged `source.extra.suppress_in_chat` are skipped.\n",
        "security": [
          {
            "gorgiasWebhookToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GorgiasWebhookPayload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Processed or skipped",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "ok",
                        "threadId",
                        "messagesAdded"
                      ],
                      "properties": {
                        "ok": {
                          "const": true,
                          "type": "boolean"
                        },
                        "threadId": {
                          "type": "string"
                        },
                        "messagesAdded": {
                          "type": "integer"
                        }
                      }
                    },
                    {
                      "type": "object",
                      "required": [
                        "ok",
                        "skipped",
                        "reason"
                      ],
                      "properties": {
                        "ok": {
                          "const": true,
                          "type": "boolean"
                        },
                        "skipped": {
                          "const": true,
                          "type": "boolean"
                        },
                        "reason": {
                          "type": "string",
                          "enum": [
                            "no_ticket_or_messages",
                            "no_matching_thread"
                          ]
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Bad or missing `token`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unauthorized"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          },
          "503": {
            "description": "Production deployment without `GORGIAS_WEBHOOK_SECRET`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "webhook_not_configured"
                }
              }
            }
          }
        },
        "parameters": []
      }
    },
    "/api/webhooks/shopify/orders": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "receiveShopifyOrderWebhook",
        "summary": "Shopify orders webhook (tags chat-attributed orders)",
        "description": "Raw JSON body (2 MB limit) verified against `x-shopify-hmac-sha256`. Orders without\n`_stateset_*` note attributes are acknowledged and skipped. Attributed orders are\nrecorded as an analytics event and tagged in Shopify; a tagging failure answers `202`.\n",
        "security": [
          {
            "shopifyWebhookHmac": []
          }
        ],
        "parameters": [
          {
            "name": "x-shopify-topic",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-shopify-shop-domain",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "A Shopify Order resource. Only `id`, `name`, `note_attributes`, `tags` and customer fields are read.",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Processed or skipped",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "ok",
                        "tagged",
                        "tags"
                      ],
                      "properties": {
                        "ok": {
                          "const": true,
                          "type": "boolean"
                        },
                        "tagged": {
                          "const": true,
                          "type": "boolean"
                        },
                        "tags": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    {
                      "type": "object",
                      "required": [
                        "ok",
                        "skipped",
                        "reason"
                      ],
                      "properties": {
                        "ok": {
                          "const": true,
                          "type": "boolean"
                        },
                        "skipped": {
                          "const": true,
                          "type": "boolean"
                        },
                        "reason": {
                          "const": "not_chat_attributed",
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "202": {
            "description": "Attributed order recorded but Shopify tagging did not succeed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "tagged",
                    "reason"
                  ],
                  "properties": {
                    "ok": {
                      "const": true,
                      "type": "boolean"
                    },
                    "tagged": {
                      "const": false,
                      "type": "boolean"
                    },
                    "reason": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Body is not valid JSON",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "example": {
                  "ok": false,
                  "error": "invalid_json"
                }
              }
            }
          },
          "401": {
            "description": "HMAC verification failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkError"
                },
                "example": {
                  "ok": false,
                  "error": "invalid_webhook_signature"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled error",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "requestId": "3f1c..."
                }
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "tags": [
          "System"
        ],
        "operationId": "getHealth",
        "summary": "Liveness probe",
        "security": [],
        "responses": {
          "200": {
            "description": "Process is up",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "status",
                    "uptime",
                    "provider",
                    "store",
                    "ready"
                  ],
                  "properties": {
                    "ok": {
                      "const": true,
                      "type": "boolean"
                    },
                    "status": {
                      "const": "ok",
                      "type": "string"
                    },
                    "uptime": {
                      "type": "number",
                      "description": "Seconds since process start."
                    },
                    "provider": {
                      "$ref": "#/components/schemas/Provider"
                    },
                    "store": {
                      "$ref": "#/components/schemas/StoreKind"
                    },
                    "ready": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": []
      }
    },
    "/api/ready": {
      "get": {
        "tags": [
          "System"
        ],
        "operationId": "getReadiness",
        "summary": "Readiness probe (503 until stores are initialised)",
        "security": [],
        "responses": {
          "200": {
            "description": "Ready",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Readiness"
                }
              }
            }
          },
          "503": {
            "description": "Not ready",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Readiness"
                }
              }
            }
          }
        },
        "parameters": []
      }
    },
    "/api/metrics": {
      "get": {
        "tags": [
          "System"
        ],
        "operationId": "getMetrics",
        "summary": "Prometheus exposition",
        "description": "When `METRICS_TOKEN` is set the caller must present it in `x-metrics-token`; otherwise\nonly loopback / RFC 1918 callers are served and everyone else gets `403`.\n",
        "security": [
          {
            "metricsToken": []
          },
          {}
        ],
        "responses": {
          "200": {
            "description": "Prometheus text exposition",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "text/plain; version=0.0.4; charset=utf-8": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or wrong `x-metrics-token`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unauthorized"
                }
              }
            }
          },
          "403": {
            "description": "Public caller without a metrics token configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "forbidden"
                }
              }
            }
          }
        },
        "parameters": []
      }
    },
    "/api/admin/org": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Org record and counts",
        "responses": {
          "200": {
            "description": "The org record plus counts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "counts": {
                      "type": "object",
                      "properties": {
                        "sites": {
                          "type": "integer"
                        },
                        "activeKeys": {
                          "type": "integer"
                        }
                      },
                      "description": "Row counts for the org"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/keys": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "List admin keys",
        "responses": {
          "200": {
            "description": "Array of keys (never the secret)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "label": {
                        "type": "string"
                      },
                      "createdAt": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "revokedAt": {
                        "type": "string",
                        "format": "date-time"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Create an admin key",
        "description": "With a bearer key for your own org, or \u2014 once, for a new org \u2014 `bootstrapSecret` (the server's `ADMIN_BOOTSTRAP_SECRET`) and no bearer. The key is returned once.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdminKeyCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminKey"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/api/admin/keys/{id}": {
      "delete": {
        "tags": [
          "Admin"
        ],
        "summary": "Revoke an admin key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Key id"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/sites": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "List sites",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteList"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Create a site",
        "description": "Body: `{ platform, domain, \u2026 }`.",
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Site"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/sites/{id}": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Get a site",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Site"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      },
      "patch": {
        "tags": [
          "Admin"
        ],
        "summary": "Update a site",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Site"
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      },
      "delete": {
        "tags": [
          "Admin"
        ],
        "summary": "Delete a site",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/sites/{id}/config": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Draft and published config",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteConfig"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      },
      "put": {
        "tags": [
          "Admin"
        ],
        "summary": "Save the draft config",
        "description": "Body: `{ config }`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          }
        ],
        "responses": {
          "200": {
            "description": "The saved draft",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "integer"
                    },
                    "config": {
                      "type": "object",
                      "description": "The site's widget settings \u2014 free-form, whatever the editor saved"
                    },
                    "publishedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "`null` until published"
                    }
                  },
                  "required": [
                    "version",
                    "config"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/sites/{id}/config/publish": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Publish the draft",
        "description": "Publish the saved draft. Returns 400 no_draft_to_publish when no pending draft exists.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          }
        ],
        "responses": {
          "200": {
            "description": "The published config",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "integer"
                    },
                    "config": {
                      "type": "object",
                      "description": "The site's widget settings \u2014 free-form, whatever the editor saved"
                    },
                    "publishedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "`null` until published"
                    }
                  },
                  "required": [
                    "version",
                    "config"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/sites/{id}/config/versions": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "List config versions",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          }
        ],
        "responses": {
          "200": {
            "description": "All versions, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "version": {
                        "type": "integer"
                      },
                      "config": {
                        "type": "object",
                        "description": "The site's widget settings \u2014 free-form, whatever the editor saved"
                      },
                      "publishedAt": {
                        "type": "string",
                        "format": "date-time",
                        "description": "`null` until published"
                      }
                    },
                    "required": [
                      "version",
                      "config"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/sites/{id}/config/versions/{version}": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Read one config version",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Version number"
          }
        ],
        "responses": {
          "200": {
            "description": "One config version",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "integer"
                    },
                    "config": {
                      "type": "object",
                      "description": "The site's widget settings \u2014 free-form, whatever the editor saved"
                    },
                    "publishedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "`null` until published"
                    }
                  },
                  "required": [
                    "version",
                    "config"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/sites/{id}/config/rollback": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Roll back to a version",
        "description": "Body: `{ version }`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          }
        ],
        "responses": {
          "200": {
            "description": "The restored config",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "integer"
                    },
                    "config": {
                      "type": "object",
                      "description": "The site's widget settings \u2014 free-form, whatever the editor saved"
                    },
                    "publishedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "`null` until published"
                    }
                  },
                  "required": [
                    "version",
                    "config"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/sites/{id}/install/snippet": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Generate the install snippet",
        "description": "Body: `{ platform }`. Returns `{ snippet, instructions }`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          }
        ],
        "responses": {
          "200": {
            "description": "Snippet and instructions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "snippet": {
                      "type": "string",
                      "description": "The <script> tag to paste"
                    },
                    "instructions": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "snippet"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/sites/{id}/install/shopify": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Install on a Shopify store",
        "description": "Uses the site's stored `accessToken`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          }
        ],
        "responses": {
          "200": {
            "description": "Install result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "method": {
                      "type": "string",
                      "enum": [
                        "script_tag"
                      ]
                    },
                    "scriptTagId": {
                      "type": "integer"
                    },
                    "note": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string",
                      "description": "On failure (`ok: false`): `unsafe_domain`, `unsafe_address`, `shopify_unreachable`, or the Shopify API error"
                    }
                  },
                  "required": [
                    "ok"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/sites/{id}/verify": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Verify the live install",
        "description": "Checks the live page for the snippet.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          }
        ],
        "responses": {
          "200": {
            "description": "Verification result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installed": {
                      "type": "boolean"
                    },
                    "checkedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "detail": {
                      "type": "string"
                    }
                  },
                  "description": "Checks the live page for the snippet"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/sites/{id}/analytics": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Site-scoped thread analytics",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Site id"
          }
        ],
        "responses": {
          "200": {
            "description": "Site-scoped thread analytics",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "skipped": {
                      "type": "boolean"
                    },
                    "orgId": {
                      "type": "string"
                    },
                    "siteId": {
                      "type": "string",
                      "description": "`null` for an org-wide scope"
                    },
                    "scope": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string"
                    },
                    "range": {
                      "type": "object",
                      "description": "`null` unless a range was requested"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "analysis": {
                      "type": "object",
                      "description": "The thread-analytics report (same shape as the ResponseCX thread-analytics `analysis`)"
                    }
                  },
                  "required": [
                    "skipped"
                  ],
                  "description": "When analytics is unavailable the body is `{ \"skipped\": true, \"reason\": \"analytics_unavailable\" }`."
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/oauth/grants": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "List OAuth grants (MCP connector)",
        "responses": {
          "200": {
            "description": "Array of grants",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "clientId": {
                        "type": "string"
                      },
                      "createdAt": {
                        "type": "string",
                        "format": "date-time"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/oauth/grants/{id}": {
      "delete": {
        "tags": [
          "Admin"
        ],
        "summary": "Revoke an OAuth grant",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Grant id"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "admin_key": []
          }
        ]
      }
    },
    "/api/admin/live-chat": {
      "get": {
        "tags": [
          "Live chat"
        ],
        "summary": "List native operator conversations",
        "description": "Scans the latest 500 active threads in the shared store, then filters by verified tenant ownership. Ownerless legacy threads are excluded. This bounded inbox is not a historical archive. Requires WIDGET_LIVE_CHAT_ENABLED=true, an admin bearer key and verified thread ownership. Responses use Cache-Control: no-store. Mutations share the customer/AI thread execution lease. Limited to 120 requests/minute per credential (shared with Redis when configured).",
        "security": [
          {
            "admin_key": []
          }
        ],
        "responses": {
          "200": {
            "description": "Persisted result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LiveChatSummary"
                      }
                    },
                    "scanned": {
                      "type": "integer"
                    },
                    "windowLimit": {
                      "const": 500,
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid admin key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Thread not found in the authenticated organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Claim, content-ID, note-limit or thread lease conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Live chat disabled or persistence unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/admin/live-chat/{id}": {
      "get": {
        "tags": [
          "Live chat"
        ],
        "summary": "Read a native operator conversation",
        "description": "Tenant-scoped transcript without thread capability secrets or private notes. Requires WIDGET_LIVE_CHAT_ENABLED=true, an admin bearer key and verified thread ownership. Responses use Cache-Control: no-store. Mutations share the customer/AI thread execution lease. Limited to 120 requests/minute per credential (shared with Redis when configured).",
        "security": [
          {
            "admin_key": []
          }
        ],
        "responses": {
          "200": {
            "description": "Persisted result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "thread": {
                      "$ref": "#/components/schemas/LiveChatSummary"
                    },
                    "messages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OperatorMessage"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid admin key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Thread not found in the authenticated organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Claim, content-ID, note-limit or thread lease conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Live chat disabled or persistence unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Native conversation ID"
          }
        ]
      }
    },
    "/api/admin/live-chat/{id}/notes": {
      "get": {
        "tags": [
          "Live chat"
        ],
        "summary": "Read private operator notes",
        "description": "Private notes are available to operators in the owning organization, separately from customer transcripts. Requires WIDGET_LIVE_CHAT_ENABLED=true, an admin bearer key and verified thread ownership. Responses use Cache-Control: no-store. Mutations share the customer/AI thread execution lease. Limited to 120 requests/minute per credential (shared with Redis when configured).",
        "security": [
          {
            "admin_key": []
          }
        ],
        "responses": {
          "200": {
            "description": "Persisted result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "notes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OperatorNote"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid admin key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Thread not found in the authenticated organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Claim, content-ID, note-limit or thread lease conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Live chat disabled or persistence unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Native conversation ID"
          }
        ]
      },
      "post": {
        "tags": [
          "Live chat"
        ],
        "summary": "Save a private operator note",
        "description": "Does not require an active claim. At most 100 notes and 64,000 characters per thread. Identical ID/content/operator replays; changed content returns note_id_conflict. Notes are excluded from customer messages and model context. Requires WIDGET_LIVE_CHAT_ENABLED=true, an admin bearer key and verified thread ownership. Responses use Cache-Control: no-store. Mutations share the customer/AI thread execution lease. Limited to 120 requests/minute per credential (shared with Redis when configured).",
        "security": [
          {
            "admin_key": []
          }
        ],
        "responses": {
          "200": {
            "description": "Persisted result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "note": {
                      "$ref": "#/components/schemas/OperatorNote"
                    },
                    "replayed": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid operator name, message or note",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid admin key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Thread not found in the authenticated organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Claim, content-ID, note-limit or thread lease conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Live chat disabled or persistence unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Native conversation ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text",
                  "operatorName",
                  "clientNoteId"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 4000,
                    "example": "Waiting for the carrier update before replying."
                  },
                  "operatorName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80,
                    "description": "Nonblank operator display name; control characters are rejected.",
                    "example": "Alex"
                  },
                  "clientNoteId": {
                    "type": "string",
                    "pattern": "^[\\w-]{8,128}$",
                    "minLength": 8,
                    "maxLength": 128,
                    "description": "Stable ID for this intentional action. Reuse only with identical content when recovering delivery.",
                    "example": "operator_note_20261020_001"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/admin/live-chat/{id}/claim": {
      "post": {
        "tags": [
          "Live chat"
        ],
        "summary": "Take over a native conversation",
        "description": "Claims for two minutes under the authenticated operator key. Another active owner returns conversation_claimed. Customer sends are acknowledged without an AI reply while the claim remains active. Requires WIDGET_LIVE_CHAT_ENABLED=true, an admin bearer key and verified thread ownership. Responses use Cache-Control: no-store. Mutations share the customer/AI thread execution lease. Limited to 120 requests/minute per credential (shared with Redis when configured).",
        "security": [
          {
            "admin_key": []
          }
        ],
        "responses": {
          "200": {
            "description": "Persisted result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "thread": {
                      "$ref": "#/components/schemas/LiveChatSummary"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid operator name, message or note",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid admin key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Thread not found in the authenticated organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Claim, content-ID, note-limit or thread lease conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Live chat disabled or persistence unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Native conversation ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "operatorName"
                ],
                "properties": {
                  "operatorName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80,
                    "description": "Nonblank operator display name; control characters are rejected."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/admin/live-chat/{id}/heartbeat": {
      "post": {
        "tags": [
          "Live chat"
        ],
        "summary": "Renew a native operator claim",
        "description": "Only the owning operator key can extend an active claim for another two minutes. The terminal renews every 30 seconds. Requires WIDGET_LIVE_CHAT_ENABLED=true, an admin bearer key and verified thread ownership. Responses use Cache-Control: no-store. Mutations share the customer/AI thread execution lease. Limited to 120 requests/minute per credential (shared with Redis when configured).",
        "security": [
          {
            "admin_key": []
          }
        ],
        "responses": {
          "200": {
            "description": "Persisted result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "thread": {
                      "$ref": "#/components/schemas/LiveChatSummary"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid admin key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Thread not found in the authenticated organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Claim, content-ID, note-limit or thread lease conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Live chat disabled or persistence unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Native conversation ID"
          }
        ]
      }
    },
    "/api/admin/live-chat/{id}/messages": {
      "post": {
        "tags": [
          "Live chat"
        ],
        "summary": "Send a native operator reply",
        "description": "Requires the owning active claim for a new reply. Identical stored ID/content/operator replays before claim validation, even after release. Changed text or operator returns message_id_conflict. Acknowledged only after thread persistence. Requires WIDGET_LIVE_CHAT_ENABLED=true, an admin bearer key and verified thread ownership. Responses use Cache-Control: no-store. Mutations share the customer/AI thread execution lease. Limited to 120 requests/minute per credential (shared with Redis when configured).",
        "security": [
          {
            "admin_key": []
          }
        ],
        "responses": {
          "200": {
            "description": "Persisted result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "$ref": "#/components/schemas/OperatorMessage"
                    },
                    "replayed": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid operator name, message or note",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid admin key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Thread not found in the authenticated organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Claim, content-ID, note-limit or thread lease conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Live chat disabled or persistence unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Native conversation ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text",
                  "clientMessageId"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 8000,
                    "example": "I am checking the shipment status."
                  },
                  "clientMessageId": {
                    "type": "string",
                    "pattern": "^[\\w-]{8,128}$",
                    "minLength": 8,
                    "maxLength": 128,
                    "description": "Stable ID for this intentional action. Reuse only with identical content when recovering delivery.",
                    "example": "operator_reply_20261020_001"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/admin/live-chat/{id}/release": {
      "post": {
        "tags": [
          "Live chat"
        ],
        "summary": "Return a native conversation to AI",
        "description": "Requires the owning active claim; appends the operator release event and resumes AI. Requires WIDGET_LIVE_CHAT_ENABLED=true, an admin bearer key and verified thread ownership. Responses use Cache-Control: no-store. Mutations share the customer/AI thread execution lease. Limited to 120 requests/minute per credential (shared with Redis when configured).",
        "security": [
          {
            "admin_key": []
          }
        ],
        "responses": {
          "200": {
            "description": "Persisted result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "thread": {
                      "$ref": "#/components/schemas/LiveChatSummary"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid admin key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Thread not found in the authenticated organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Claim, content-ID, note-limit or thread lease conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Live chat disabled or persistence unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Native conversation ID"
          }
        ]
      }
    },
    "/api/admin/live-chat/{id}/resolve": {
      "post": {
        "tags": [
          "Live chat"
        ],
        "summary": "Resolve a native conversation",
        "description": "Requires the owning active claim; releases takeover and appends a resolution event. A subsequent customer question can resume AI. Requires WIDGET_LIVE_CHAT_ENABLED=true, an admin bearer key and verified thread ownership. Responses use Cache-Control: no-store. Mutations share the customer/AI thread execution lease. Limited to 120 requests/minute per credential (shared with Redis when configured).",
        "security": [
          {
            "admin_key": []
          }
        ],
        "responses": {
          "200": {
            "description": "Persisted result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "thread": {
                      "$ref": "#/components/schemas/LiveChatSummary"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid admin key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Thread not found in the authenticated organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Claim, content-ID, note-limit or thread lease conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Live chat disabled or persistence unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Native conversation ID"
          }
        ]
      }
    }
  }
}
