{
  "openapi": "3.1.0",
  "info": {
    "title": "Sapportly Public API",
    "version": "1.4.0",
    "description": "Публичный REST API Sapportly. Контракт клиента: sdks/SPEC.md. Канал ingest: namespace:identifier. Списки — JSON-массив; ?envelope=true или заголовок X-Sapportly-List-Envelope: 1 включает { data, has_more, next_cursor }. Legacy alias X-Supportly-List-Envelope всё ещё принимается (deprecated). Ошибки: { error, type, code, message, request_id, docs_url }. Панель оператора на этот хост не входит.",
    "contact": {
      "email": "ac@sapportly.pro",
      "url": "https://sapportly.pro"
    }
  },
  "servers": [
    {
      "url": "https://api.sapportly.pro",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Status"
    },
    {
      "name": "Ingest"
    },
    {
      "name": "Widget"
    },
    {
      "name": "Attachments"
    },
    {
      "name": "WebSocket"
    },
    {
      "name": "Channels"
    },
    {
      "name": "Conversations"
    },
    {
      "name": "Contacts"
    },
    {
      "name": "Analytics"
    },
    {
      "name": "Team"
    },
    {
      "name": "Webhooks"
    },
    {
      "name": "RAG"
    }
  ],
  "paths": {
    "/v1/status": {
      "get": {
        "tags": [
          "Status"
        ],
        "summary": "Public API version",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/ingest/messages": {
      "post": {
        "tags": [
          "Ingest"
        ],
        "summary": "Server ingest message",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IngestMessageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Duplicate (idempotent)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageAccepted"
                }
              }
            }
          },
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/bootstrap": {
      "post": {
        "tags": [
          "Widget"
        ],
        "summary": "Bootstrap visitor session (browser embed)",
        "description": "Requires valid Origin/Referer matching widget install allowlist.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WidgetBootstrapRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WidgetSessionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/embed-session": {
      "post": {
        "tags": [
          "Widget"
        ],
        "summary": "Issue visitor session (server BFF)",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WidgetEmbedSessionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WidgetSessionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/messages": {
      "post": {
        "tags": [
          "Widget"
        ],
        "summary": "Visitor send message",
        "security": [
          {
            "visitorToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WidgetSendRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WidgetSendResponse"
                }
              }
            }
          },
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WidgetSendResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Widget"
        ],
        "summary": "Visitor chat history",
        "security": [
          {
            "visitorToken": []
          }
        ],
        "parameters": [
          {
            "name": "visitor_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Message history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MessageRow"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/events": {
      "post": {
        "tags": [
          "Widget"
        ],
        "summary": "Widget analytics beacon",
        "security": [
          {
            "visitorToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WidgetEventRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/identify": {
      "post": {
        "tags": [
          "Widget"
        ],
        "summary": "Set visitor identity traits",
        "description": "Visitor JWT only. Persists email/phone/external_id for bound CRM search. Response is `{ ok: true }` and never echoes traits.",
        "security": [
          {
            "visitorToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WidgetIdentifyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WidgetIdentifyResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/attachments/intent": {
      "post": {
        "tags": [
          "Attachments"
        ],
        "summary": "Create presigned upload intent",
        "description": "Returns a presigned PUT URL. Upload bytes to upload_url, then call POST /v1/attachments/{id}/complete. Requires scope attachments:write (messages:write accepted for backward compatibility).",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AttachmentUploadIntentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentUploadIntentResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/attachments/{id}/complete": {
      "post": {
        "tags": [
          "Attachments"
        ],
        "summary": "Finalize attachment after S3 upload",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentMetaResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/attachments/{id}": {
      "get": {
        "tags": [
          "Attachments"
        ],
        "summary": "Attachment metadata",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentMetaResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/ws/ticket": {
      "post": {
        "tags": [
          "WebSocket"
        ],
        "summary": "Issue WebSocket connection ticket",
        "description": "Returns a single-use ticket and ws_url. Connect with `{ws_url}?ticket={ticket}` (ADR-003). Integrators: API key with scope ws:connect. Panel: panel JWT behind X-Slc proxy.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WsTicketResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/channels": {
      "get": {
        "tags": [
          "Channels"
        ],
        "summary": "List registered channels",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "archived"
              ]
            }
          },
          {
            "name": "namespace",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Channel list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ChannelRegistryItem"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Channels"
        ],
        "summary": "Create channel",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateChannelRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelRegistryItem"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/channels/{id}": {
      "get": {
        "tags": [
          "Channels"
        ],
        "summary": "Get channel by id",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelRegistryItem"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Channels"
        ],
        "summary": "Update channel",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateChannelRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelRegistryItem"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Channels"
        ],
        "summary": "Archive channel",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "archived": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/conversations": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "List conversation summaries",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Search filter"
          },
          {
            "name": "envelope",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "If true, wrap the page in `{ data, has_more, next_cursor }`. Same as header X-Sapportly-List-Envelope: 1."
          }
        ],
        "responses": {
          "200": {
            "description": "Conversation list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ConversationSummary"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/conversations/{channel}/messages": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Message history for a channel",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "channel",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Channel key, e.g. custom:shop or widget:{visitor_uuid}"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 500
            }
          },
          {
            "name": "envelope",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "If true, wrap the page in `{ data, has_more, next_cursor }`. Same as header X-Sapportly-List-Envelope: 1."
          }
        ],
        "responses": {
          "200": {
            "description": "Messages with attachment metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MessageRow"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Agent reply in a widget conversation",
        "description": "Channel must be `widget:{visitor_uuid}`. Requires scope conversations:write.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "channel",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConversationReplyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversationReplyAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/analytics/track": {
      "post": {
        "tags": [
          "Analytics"
        ],
        "summary": "Track widget or product analytics event",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "visitorToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "event_name"
                ],
                "properties": {
                  "event_name": {
                    "type": "string"
                  },
                  "channel": {
                    "type": "string"
                  },
                  "dimensions": {
                    "type": "object"
                  },
                  "metrics": {
                    "type": "object"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/team/roles": {
      "get": {
        "tags": [
          "Team"
        ],
        "summary": "List tenant roles (SDK routing)",
        "description": "Requires API key scope `team:read` or panel JWT.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Role list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "name": {
                        "type": "string"
                      },
                      "external_key": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/canned-prompt": {
      "post": {
        "tags": [
          "Widget"
        ],
        "summary": "Exchange canned prompt pair",
        "security": [
          {
            "visitorToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/channel-security": {
      "get": {
        "tags": [
          "Widget"
        ],
        "summary": "Channel encryption metadata for visitor",
        "security": [
          {
            "visitorToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/e2ee/start": {
      "post": {
        "tags": [
          "Widget"
        ],
        "summary": "Start E2EE session handshake",
        "security": [
          {
            "visitorToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/capability-confirm": {
      "get": {
        "tags": [
          "Widget"
        ],
        "summary": "Pending capability confirm cards for the visitor",
        "security": [
          {
            "visitorToken": []
          }
        ],
        "parameters": [
          {
            "name": "visitor_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Widget"
        ],
        "summary": "Visitor confirms or cancels a pending capability write",
        "security": [
          {
            "visitorToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "confirm_id",
                  "action",
                  "visitor_id"
                ],
                "properties": {
                  "confirm_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "action": {
                    "type": "string",
                    "enum": [
                      "confirm",
                      "cancel"
                    ]
                  },
                  "visitor_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/attachments/{id}/download": {
      "get": {
        "tags": [
          "Attachments"
        ],
        "summary": "Download attachment bytes",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "File bytes"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/attachments/upload-intent": {
      "post": {
        "tags": [
          "Attachments"
        ],
        "summary": "Alias for POST /v1/attachments/intent",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AttachmentUploadIntentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentUploadIntentResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/attachments/intent": {
      "post": {
        "tags": [
          "Attachments",
          "Widget"
        ],
        "summary": "Visitor attachment upload intent",
        "security": [
          {
            "visitorToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/attachments/{id}/complete": {
      "post": {
        "tags": [
          "Attachments",
          "Widget"
        ],
        "summary": "Finalize visitor attachment upload",
        "security": [
          {
            "visitorToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/attachments/{id}/download": {
      "get": {
        "tags": [
          "Attachments",
          "Widget"
        ],
        "summary": "Download visitor attachment",
        "security": [
          {
            "visitorToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "File bytes"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/conversations/{channel}/assignment": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Get conversation assignment",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "channel",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Assignment"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Conversations"
        ],
        "summary": "Assign conversation",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "channel",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/conversations/{channel}/transfer": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Transfer conversation assignment",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "channel",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Transferred"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/conversations/{channel}/assignment/history": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Assignment history",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "channel",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "History"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Get outbound webhook config",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Config",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookConfig"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Create outbound webhook endpoint",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEndpointCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Legacy update of the first outbound webhook",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Config",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookConfig"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{id}": {
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Update webhook endpoint",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEndpointUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete webhook endpoint",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{id}/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a test webhook delivery",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a test webhook delivery",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/rag/documents": {
      "get": {
        "tags": [
          "RAG"
        ],
        "summary": "List knowledge documents",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ documents: [...] }"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "RAG"
        ],
        "summary": "Index a knowledge document",
        "description": "JSON `{ title, body }`. Scope `attachments:write` (or `messages:write`).",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title",
                  "body"
                ],
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RagDocumentAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/rag/documents/{id}": {
      "delete": {
        "tags": [
          "RAG"
        ],
        "summary": "Delete a knowledge document",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ deleted: true }"
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/contacts": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "List visitor identities",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Contacts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Contact"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/contacts/{channel}": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "Get identity for a channel",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "channel",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Contacts"
        ],
        "summary": "Upsert identity traits for a channel",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "channel",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VisitorIdentityInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "400": {
            "description": "Bad request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error",
          "type",
          "code",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable message (compat with older clients)"
          },
          "type": {
            "type": "string",
            "enum": [
              "authentication_error",
              "permission_error",
              "invalid_request_error",
              "rate_limit_error",
              "api_error"
            ]
          },
          "code": {
            "type": "string",
            "examples": [
              "unauthorized",
              "validation_error",
              "not_found",
              "rate_limited",
              "plan_limit_exceeded"
            ]
          },
          "message": {
            "type": "string"
          },
          "request_id": {
            "type": "string"
          },
          "docs_url": {
            "type": "string",
            "format": "uri"
          },
          "param": {
            "type": "string"
          },
          "resource": {
            "type": "string"
          },
          "used": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          }
        }
      },
      "StatusResponse": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "example": "1.2.0"
          },
          "phase": {
            "type": "string",
            "example": "production"
          },
          "features": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "breaking_changes": {
            "type": "object"
          }
        }
      },
      "DeliveryChannel": {
        "type": "string",
        "enum": [
          "webhook",
          "ws",
          "rest"
        ],
        "description": "Integrator delivery channel for outbound events. Distinct from EventMetadata.source (publishing service)."
      },
      "OutboundDelivery": {
        "type": "object",
        "required": [
          "message_id",
          "delivery_id",
          "source"
        ],
        "description": "Canonical delivery context for webhook, WebSocket, and REST reconciliation. Use message_id for deduplication when dual delivery is enabled.",
        "properties": {
          "message_id": {
            "type": "string",
            "format": "uuid"
          },
          "idempotency_key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "delivery_id": {
            "type": "string",
            "format": "uuid"
          },
          "source": {
            "$ref": "#/components/schemas/DeliveryChannel"
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "required": [
          "id",
          "name",
          "enabled",
          "event_types",
          "has_secret",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "maxLength": 80
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "enabled": {
            "type": "boolean"
          },
          "event_types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "has_secret": {
            "type": "boolean"
          },
          "last_delivery_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_status": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookConfig": {
        "type": "object",
        "required": [
          "items",
          "available_event_types",
          "endpoint_limit",
          "plan"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            }
          },
          "available_event_types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "endpoint_limit": {
            "type": "integer",
            "minimum": 1
          },
          "plan": {
            "type": "string"
          }
        }
      },
      "WebhookTestResult": {
        "type": "object",
        "required": [
          "ok",
          "message"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "status_code": {
            "type": [
              "integer",
              "null"
            ]
          },
          "message": {
            "type": "string"
          }
        }
      },
      "WebhookEndpointCreate": {
        "type": "object",
        "required": [
          "name",
          "url",
          "secret",
          "event_types"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          },
          "secret": {
            "type": "string",
            "minLength": 32,
            "maxLength": 256
          },
          "enabled": {
            "type": "boolean",
            "default": false
          },
          "event_types": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 0
          }
        }
      },
      "WebhookEndpointUpdate": {
        "type": "object",
        "required": [
          "name",
          "url",
          "enabled",
          "event_types"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          },
          "enabled": {
            "type": "boolean"
          },
          "event_types": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 0
          },
          "secret": {
            "type": "string",
            "description": "Immutable; do not send after creation."
          }
        }
      },
      "ApiKeyScope": {
        "type": "string",
        "enum": [
          "messages:write",
          "widget:embed:issue",
          "attachments:write",
          "ws:connect",
          "conversations:read",
          "conversations:write",
          "conversations:assign",
          "webhooks:read",
          "webhooks:write",
          "team:read",
          "channels:read",
          "channels:write",
          "analytics:write"
        ],
        "description": "API key permission scopes (canonical list from api_key_scopes.rs)."
      },
      "IngestMessageRequest": {
        "type": "object",
        "required": [
          "body"
        ],
        "properties": {
          "channel": {
            "type": "string",
            "description": "Source `namespace:slug` (`custom:shop`) or thread `namespace:slug:{uuid}`. Do not mint a channel per person — pass `identity.external_id` or `thread_id`.",
            "example": "custom:orders",
            "maxLength": 128
          },
          "channel_slug": {
            "type": "string",
            "description": "Registered channel slug (requires channel in registry)",
            "example": "orders"
          },
          "channel_namespace": {
            "type": "string",
            "description": "Channel namespace when using channel_slug (default: custom)",
            "example": "custom"
          },
          "body": {
            "type": "string",
            "maxLength": 65535
          },
          "idempotency_key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Optional. SDK and gateway mint a UUID if omitted. Pass your own to dedupe a retried external event (`shop:{order_id}:{comment_id}`)."
          },
          "attachment_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "content_encoding": {
            "type": "string",
            "enum": [
              "plain",
              "enc_v1_standard",
              "enc_v1_e2ee"
            ]
          },
          "skip_ai": {
            "type": "boolean",
            "description": "If true, this message skips AI and RAG modules (inbox + webhooks only)."
          },
          "identity": {
            "$ref": "#/components/schemas/VisitorIdentityInput",
            "description": "Optional visitor traits. `external_id` also binds a 1:1 thread (`ns:slug:{uuid}`) via UUID v5. Not echoed in the accept response or message.received webhooks. Empty string clears a field."
          },
          "thread_id": {
            "type": "string",
            "format": "uuid",
            "description": "Explicit conversation thread UUID. If omitted, derived from `identity.external_id`. Without either, ingest writes the shared source tape (legacy)."
          }
        },
        "description": "Provide either `channel` or (`channel_slug` + optional `channel_namespace`)."
      },
      "MessageAccepted": {
        "type": "object",
        "required": [
          "accepted"
        ],
        "properties": {
          "accepted": {
            "type": "boolean"
          },
          "message_id": {
            "type": "string",
            "format": "uuid"
          },
          "event_id": {
            "type": "string",
            "format": "uuid"
          },
          "correlation_id": {
            "type": "string",
            "format": "uuid"
          },
          "duplicate": {
            "type": "boolean"
          },
          "delivery": {
            "$ref": "#/components/schemas/OutboundDelivery"
          },
          "channel": {
            "type": "string",
            "description": "Bound conversation key after thread attach (`custom:shop:{uuid}`).",
            "maxLength": 128
          },
          "source_channel": {
            "type": "string",
            "description": "Registry/analytics key (`custom:shop`)."
          },
          "thread_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "RagDocumentAccepted": {
        "type": "object",
        "required": [
          "id",
          "title",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "chunk_count": {
            "type": "integer"
          }
        }
      },
      "WidgetBootstrapRequest": {
        "type": "object",
        "required": [
          "site_id"
        ],
        "properties": {
          "site_id": {
            "type": "string",
            "pattern": "^wgt_[A-Za-z0-9]{16}$"
          },
          "visitor_id": {
            "type": "string",
            "format": "uuid"
          },
          "config_etag": {
            "type": "string"
          }
        }
      },
      "WidgetEmbedSessionRequest": {
        "type": "object",
        "properties": {
          "visitor_id": {
            "type": "string",
            "format": "uuid"
          },
          "config_etag": {
            "type": "string"
          }
        }
      },
      "WidgetSessionRequest": {
        "type": "object",
        "deprecated": true,
        "description": "Deprecated alias for WidgetEmbedSessionRequest (removed /v1/widget/session).",
        "properties": {
          "visitor_id": {
            "type": "string",
            "format": "uuid"
          },
          "config_etag": {
            "type": "string"
          }
        }
      },
      "WidgetSessionResponse": {
        "type": "object",
        "required": [
          "visitor_id",
          "token",
          "ws_ticket",
          "ws_url"
        ],
        "properties": {
          "visitor_id": {
            "type": "string",
            "format": "uuid"
          },
          "token": {
            "type": "string",
            "description": "Visitor JWT for X-Visitor-Token"
          },
          "ws_ticket": {
            "type": "string",
            "format": "uuid"
          },
          "ws_url": {
            "type": "string",
            "format": "uri"
          },
          "config": {
            "type": "object"
          },
          "etag": {
            "type": "string"
          },
          "preset_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "WidgetSendRequest": {
        "type": "object",
        "required": [
          "visitor_id",
          "body"
        ],
        "properties": {
          "visitor_id": {
            "type": "string",
            "format": "uuid"
          },
          "body": {
            "type": "string"
          },
          "idempotency_key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "attachment_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "content_encoding": {
            "type": "string"
          }
        }
      },
      "WidgetSendResponse": {
        "type": "object",
        "required": [
          "accepted"
        ],
        "properties": {
          "accepted": {
            "type": "boolean"
          },
          "duplicate": {
            "type": "boolean"
          }
        }
      },
      "WidgetEventRequest": {
        "type": "object",
        "required": [
          "event_name"
        ],
        "properties": {
          "event_name": {
            "type": "string",
            "example": "widget.opened"
          },
          "visitor_id": {
            "type": "string",
            "format": "uuid"
          },
          "preset_id": {
            "type": "string",
            "format": "uuid"
          },
          "properties": {
            "type": "object"
          }
        }
      },
      "VisitorIdentityInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "email": {
            "type": "string",
            "maxLength": 320,
            "description": "Visitor email. Empty string clears the stored value."
          },
          "phone": {
            "type": "string",
            "maxLength": 32,
            "description": "Visitor phone (digits, +, -, space). Empty string clears."
          },
          "external_id": {
            "type": "string",
            "maxLength": 128,
            "description": "External CRM / messenger id. Empty string clears."
          }
        }
      },
      "WidgetIdentifyRequest": {
        "type": "object",
        "required": [
          "visitor_id"
        ],
        "additionalProperties": false,
        "properties": {
          "visitor_id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "maxLength": 320
          },
          "phone": {
            "type": "string",
            "maxLength": 32
          },
          "external_id": {
            "type": "string",
            "maxLength": 128
          }
        }
      },
      "WidgetIdentifyResponse": {
        "type": "object",
        "required": [
          "ok"
        ],
        "additionalProperties": false,
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "description": "Identify acknowledgement. Does not echo email/phone/external_id."
      },
      "MessageRow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "channel": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "idempotency_key": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "user_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "AttachmentPublic": {
        "type": "object",
        "required": [
          "id",
          "original_name",
          "mime_type",
          "size_bytes",
          "kind"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "original_name": {
            "type": "string"
          },
          "mime_type": {
            "type": "string"
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64"
          },
          "kind": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "audio",
              "document",
              "other"
            ]
          },
          "download_url": {
            "type": "string",
            "format": "uri",
            "nullable": true
          }
        }
      },
      "AttachmentMetaResponse": {
        "type": "object",
        "required": [
          "attachment"
        ],
        "properties": {
          "attachment": {
            "$ref": "#/components/schemas/AttachmentPublic"
          }
        }
      },
      "AttachmentUploadIntentRequest": {
        "type": "object",
        "required": [
          "filename",
          "mime_type",
          "size_bytes",
          "channel"
        ],
        "properties": {
          "filename": {
            "type": "string",
            "maxLength": 255
          },
          "mime_type": {
            "type": "string"
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "sha256": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$"
          },
          "channel": {
            "type": "string",
            "description": "Target channel (namespace:identifier), e.g. custom:orders"
          }
        }
      },
      "AttachmentUploadIntentResponse": {
        "type": "object",
        "required": [
          "attachment_id",
          "upload_url",
          "expires_in_secs",
          "headers"
        ],
        "properties": {
          "attachment_id": {
            "type": "string",
            "format": "uuid"
          },
          "upload_url": {
            "type": "string",
            "format": "uri"
          },
          "expires_in_secs": {
            "type": "integer",
            "format": "int64"
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Headers to send with the presigned PUT (e.g. Content-Type)"
          }
        }
      },
      "WsTicketResponse": {
        "type": "object",
        "required": [
          "ticket",
          "ws_url",
          "expires_in_secs"
        ],
        "properties": {
          "ticket": {
            "type": "string",
            "format": "uuid",
            "description": "Single-use opaque ticket (~60s TTL)"
          },
          "ws_url": {
            "type": "string",
            "format": "uri",
            "example": "wss://ws.sapportly.pro/ws"
          },
          "expires_in_secs": {
            "type": "integer",
            "format": "int64",
            "example": 60
          }
        }
      },
      "ConversationSummary": {
        "type": "object",
        "required": [
          "channel",
          "last_message",
          "last_at",
          "message_id",
          "encryption_level",
          "is_secret"
        ],
        "properties": {
          "channel": {
            "type": "string",
            "example": "custom:shop:550e8400-e29b-41d4-a716-446655440000",
            "description": "Conversation tape key. Thread `ns:slug:{uuid}` or source `ns:slug`.",
            "maxLength": 128
          },
          "visitor_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "last_message": {
            "type": "string"
          },
          "last_at": {
            "type": "string",
            "format": "date-time"
          },
          "message_id": {
            "type": "string",
            "format": "uuid"
          },
          "encryption_level": {
            "type": "string",
            "enum": [
              "standard",
              "e2ee"
            ]
          },
          "is_secret": {
            "type": "boolean"
          },
          "external_id": {
            "type": "string",
            "nullable": true,
            "description": "Visitor `identity.external_id` persisted on this thread."
          }
        }
      },
      "Contact": {
        "type": "object",
        "required": [
          "channel",
          "updated_at"
        ],
        "properties": {
          "channel": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "external_id": {
            "type": "string",
            "nullable": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ListEnvelope": {
        "type": "object",
        "required": [
          "data",
          "has_more"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {}
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "ConversationReplyRequest": {
        "type": "object",
        "required": [
          "body"
        ],
        "properties": {
          "body": {
            "type": "string"
          },
          "idempotency_key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "attachment_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "content_encoding": {
            "type": "string"
          }
        }
      },
      "ConversationReplyAccepted": {
        "type": "object",
        "required": [
          "accepted",
          "message_id"
        ],
        "properties": {
          "accepted": {
            "type": "boolean"
          },
          "message_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "InboxUnreadResponse": {
        "type": "object",
        "required": [
          "total"
        ],
        "properties": {
          "total": {
            "type": "integer",
            "format": "int64"
          }
        },
        "description": "Panel-only — not in public OpenAPI (see packages/panel/openapi.json)."
      },
      "MarkConversationReadRequest": {
        "type": "object",
        "properties": {
          "message_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "MarkConversationReadResponse": {
        "type": "object",
        "required": [
          "ok",
          "last_read_at"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "last_read_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ChannelRegistryItem": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "namespace",
          "channel_key",
          "display_name",
          "channel_type",
          "status",
          "message_count"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "slug": {
            "type": "string"
          },
          "namespace": {
            "type": "string"
          },
          "channel_key": {
            "type": "string"
          },
          "display_name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "channel_type": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          },
          "config": {
            "type": "object",
            "additionalProperties": true
          },
          "flow_rule_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "message_count": {
            "type": "integer"
          },
          "last_message_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateChannelRequest": {
        "type": "object",
        "required": [
          "slug",
          "namespace",
          "display_name"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "pattern": "^[a-z0-9_-]{1,48}$"
          },
          "namespace": {
            "type": "string",
            "enum": [
              "api",
              "custom",
              "telegram",
              "email",
              "slack",
              "discord"
            ]
          },
          "display_name": {
            "type": "string",
            "maxLength": 128
          },
          "description": {
            "type": "string",
            "maxLength": 512
          },
          "config": {
            "type": "object",
            "additionalProperties": true
          },
          "bot_token": {
            "type": "string",
            "writeOnly": true,
            "description": "Telegram bot token. Stored in channel_secrets vault, never in config JSON."
          }
        }
      },
      "UpdateChannelRequest": {
        "type": "object",
        "properties": {
          "display_name": {
            "type": "string",
            "maxLength": 128
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "config": {
            "type": "object",
            "additionalProperties": true
          },
          "flow_rule_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          },
          "bot_token": {
            "type": "string",
            "writeOnly": true,
            "description": "Telegram bot token. Stored in channel_secrets vault, never in config JSON."
          }
        }
      }
    },
    "securitySchemes": {
      "apiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Tenant API key: sk_live_…"
      },
      "visitorToken": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Visitor-Token",
        "description": "Visitor JWT from widget session (with API key)"
      }
    }
  }
}