STIX CHATAPI v1

Интеграции без открытых сообщений.

Управляйте чатами, участниками, сообщениями, файлами и уведомлениями через единый REST API. Шифрование и расшифровка выполняются в вашем доверенном клиенте.

Скачать OpenAPI 3.1

Авторизация

Создайте API-токен в настройках приложения. Передавайте его в заголовке Authorization: Bearer <token>. Scope read разрешает чтение, write — изменение; для обоих действий нужны оба scope. Срок токена — до 365 дней. Управление токенами требует обычной сессии.

curl https://chat.example.com/api/v1/chats \
  -H 'Authorization: Bearer YOUR_TOKEN'

Все идентификаторы — UUID, даты — RFC3339, JSON — camelCase. Ошибки: {"error":{"code":"...","message":"Сообщение на русском"}}.

Как отправить сообщение

  1. Получите актуальный чат и публичные ключи участников.
  2. Зашифруйте файлы отдельными AES-256-GCM ключами, загрузите ciphertext и получите ID.
  3. Зашифруйте MessageContent свежим ключом AES-256-GCM; оберните ключ для каждого участника через RSA-OAEP SHA-256.
  4. Подпишите envelope ключом отправителя ECDSA P-256 и отправьте его вместе с clientId и ID вложений.

Ключи API-токена недостаточно для расшифровки. Приватные ключи аккаунта должны оставаться в доверенной среде интеграции. Recovery key у новых аккаунтов — 5 английских слов; старые base64url ключи остаются совместимы. Точная канонизация подписи и AAD описана в описании протокола. Схемы MessageContent и Attachment относятся к расшифрованным данным клиента.

Онлайн-консультации

Зарегистрированный оператор создаёт модуль POST /api/v1/livechat/site и вставляет выданный скрипт на сайт. Виджет открывается в iframe, создаёт гостевого посетителя и групповой чат с оператором. Переписка использует тот же E2EE envelope; сервер хранит только зашифрованные сообщения.

AI-ассистент

POST /api/v1/ai/messages отправляет текст в закреплённый чат STIX AI. Этот отдельный чат не является E2EE: его текст нужен серверу для запроса к OpenAI. Обычные личные, групповые и livechat-чаты ассистенту недоступны.

Обновления и повтор запросов

SSE доступен через GET /api/v1/events с Authorization. После переподключения перечитайте REST-данные. Повтор отправки с тем же clientId идемпотентен. При изменении состава группы получите чат снова и заново зашифруйте сообщение для актуальных участников.

Методы

GET/api/v1/configКонфигурация клиента

Публичный метод

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Config"
        }
      }
    }
  },
  "503": {
    "$ref": "#/components/responses/Unavailable"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  }
}
GET/api/v1/statsПубличная статистика

Публичный метод

Возвращает только агрегированное число активных обычных зарегистрированных аккаунтов. Персональные данные не раскрываются.

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/PublicStats"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
POST/api/v1/auth/start-loginНачать вход без SMS

Публичный метод

Публичный шаг входа. Если номер зарегистрирован, сервер возвращает профиль, keyBackup и одноразовый challenge. Клиент расшифровывает keyBackup локально ключом из 5 слов или QR-переносом и подписывает challenge приватным P-256 ключом. SMS не отправляется. Если номера нет, вернётся registered=false, после чего клиент запрашивает SMS для регистрации.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "phone": {
            "type": "string",
            "pattern": "^\\+[1-9][0-9]{7,14}$",
            "examples": [
              "+79991234567"
            ]
          }
        },
        "required": [
          "phone"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/StartLoginResult"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "503": {
    "$ref": "#/components/responses/Unavailable"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  }
}
POST/api/v1/auth/complete-loginЗавершить вход подписью ключа

Публичный метод

Клиент подписывает UTF-8 payload `STIX Chat login v1 {challengeId} {challenge}` приватным P-256 ключом аккаунта. Подпись передаётся как 64 байт r||s в unpadded base64url. Recovery key никогда не отправляется на сервер.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "challengeId": {
            "type": "string",
            "format": "uuid"
          },
          "signature": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]+$"
          }
        },
        "required": [
          "challengeId",
          "signature"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Session"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "503": {
    "$ref": "#/components/responses/Unavailable"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  }
}
POST/api/v1/auth/request-codeОтправить код по SMS

Публичный метод

Нормализованный E.164. SMS-код нужен для регистрации нового пользователя и подтверждения восстановления/QR на уже авторизованном устройстве. Обычный вход существующего аккаунта использует start-login/complete-login без SMS. Код из 6 цифр действует 5 минут, максимум 5 попыток. Лимиты по IP и телефону. dev-file разрешён только в development; код не возвращается в HTTP.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "phone": {
            "type": "string",
            "pattern": "^\\+[1-9][0-9]{7,14}$",
            "examples": [
              "+79991234567"
            ]
          }
        },
        "required": [
          "phone"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Challenge"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "503": {
    "$ref": "#/components/responses/Unavailable"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  }
}
POST/api/v1/auth/verify-codeПроверить SMS-код

Публичный метод

Код расходуется атомарно. Для нового номера возвращается registrationToken на 10 минут. Ответ с сессией для существующего аккаунта сохранён только для совместимости старых клиентов; новый вход использует start-login/complete-login без SMS.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "challengeId": {
            "type": "string",
            "format": "uuid"
          },
          "code": {
            "type": "string",
            "pattern": "^[0-9]{6}$"
          }
        },
        "required": [
          "challengeId",
          "code"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "oneOf": [
            {
              "$ref": "#/components/schemas/Session"
            },
            {
              "$ref": "#/components/schemas/RegistrationRequired"
            }
          ]
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  }
}
POST/api/v1/auth/registerЗавершить регистрацию

Публичный метод

Username приводится к нижнему регистру и уникален. Ключи создаются на клиенте. Сервер получает только публичные ключи и зашифрованную резервную копию.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "registrationToken": {
            "type": "string"
          },
          "username": {
            "type": "string",
            "minLength": 3,
            "maxLength": 32,
            "pattern": "^[a-zA-Z0-9_]+$"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "publicKey": {
            "$ref": "#/components/schemas/PublicKeys"
          },
          "keyBackup": {
            "$ref": "#/components/schemas/KeyBackup"
          }
        },
        "required": [
          "registrationToken",
          "username",
          "name",
          "publicKey",
          "keyBackup"
        ]
      }
    }
  }
}

Ответы

{
  "201": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Session"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "409": {
    "$ref": "#/components/responses/Conflict"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  }
}
POST/api/v1/auth/logoutОтозвать текущую сессию

Только сессия

Ответы

{
  "204": {
    "description": "Успешно, без тела ответа."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
GET/api/v1/meТекущий профиль и резервная копия ключей

Сессия или API-токен

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/User"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
PATCH/api/v1/meИзменить профиль

Сессия или API-токен

Ключи идентичности и телефон этим методом не изменяются.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "username": {
            "type": "string",
            "minLength": 3,
            "maxLength": 32,
            "pattern": "^[a-zA-Z0-9_]+$"
          },
          "avatarUrl": {
            "type": "string",
            "maxLength": 131072,
            "description": "Small public data URL. Empty string removes the avatar."
          }
        },
        "required": [],
        "minProperties": 1
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/User"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "409": {
    "$ref": "#/components/responses/Conflict"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
DELETE/api/v1/meНавсегда удалить аккаунт

Только сессия

Удаляет профиль, телефон, ключи, сессии, свои сообщения и файлы. Личные/AI/livechat-чаты удаляются; в группах сохраняются сообщения других участников и назначается новый владелец. Удаление ciphertext из хранилища имеет durable retry. Сохранённые получателями копии и резервные копии до истечения срока хранения отозвать невозможно.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "confirmation": {
            "type": "string",
            "const": "DELETE"
          }
        },
        "required": [
          "confirmation"
        ]
      }
    }
  }
}

Ответы

{
  "204": {
    "description": "Успешно, без тела ответа."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
PUT/api/v1/me/key-backupОбновить резервную копию ключей

Только сессия

Только пользовательская сессия. Клиент сначала локально проверяет текущий recovery key, затем загружает заново зашифрованную резервную копию. Публичная identity и история сообщений не меняются.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "keyBackup": {
            "$ref": "#/components/schemas/KeyBackup"
          }
        },
        "required": [
          "keyBackup"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/User"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
PUT/api/v1/me/identityСбросить криптографическую identity

Только сессия

Только пользовательская сессия и явное действие пользователя при полной потере recovery key и всех устройств. Создаёт новые публичные ключи и encrypted backup для будущих сообщений; старую E2EE-историю этим методом расшифровать нельзя.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "publicKey": {
            "$ref": "#/components/schemas/PublicKeys"
          },
          "keyBackup": {
            "$ref": "#/components/schemas/KeyBackup"
          }
        },
        "required": [
          "publicKey",
          "keyBackup"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/User"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
GET/api/v1/blocksСписок заблокированных пользователей

Сессия или API-токен

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/PublicUser"
          }
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
PUT/api/v1/blocks/{userId}Заблокировать пользователя

Сессия или API-токен

Сервер запрещает отправку и загрузку вложений между заблокированной парой, включая общие группы. Создание чата или добавление участников с блокировкой запрещается. История сохраняется; остальные участники группы продолжают переписку.

Параметры

[
  {
    "name": "userId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Ответы

{
  "204": {
    "description": "Успешно, без тела ответа."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
DELETE/api/v1/blocks/{userId}Снять свою блокировку

Сессия или API-токен

Параметры

[
  {
    "name": "userId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Ответы

{
  "204": {
    "description": "Успешно, без тела ответа."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
POST/api/v1/reportsПожаловаться модератору

Только сессия

Только собеседник может пожаловаться. messageId требует chatId и должен быть видимым сообщением указанного отправителя. Жалоба на системного AI-ассистента допускается только с messageId его ответа в своём AI-чате. details/evidenceText — добровольно переданные модератору незашифрованные данные; клиент обязан ясно запросить согласие. Лимит 20 жалоб в сутки. Прочие сообщения сервер не расшифровывает.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "userId": {
            "type": "string",
            "format": "uuid"
          },
          "chatId": {
            "type": "string",
            "format": "uuid"
          },
          "messageId": {
            "type": "string",
            "format": "uuid"
          },
          "reason": {
            "type": "string",
            "enum": [
              "spam",
              "harassment",
              "illegal",
              "sexual",
              "violence",
              "other"
            ]
          },
          "details": {
            "type": "string",
            "maxLength": 2000
          },
          "evidenceText": {
            "type": "string",
            "maxLength": 10000
          }
        },
        "required": [
          "userId",
          "reason"
        ]
      }
    }
  }
}

Ответы

{
  "201": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "id": {
              "type": "string",
              "format": "uuid"
            },
            "status": {
              "type": "string",
              "const": "pending"
            }
          },
          "required": [
            "id",
            "status"
          ]
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
PUT/api/v1/push/apnsЗарегистрировать native iOS push

Только сессия

Уведомления содержат только общий текст, chatId и userId. Регистрация токена переводит уведомления этого устройства на текущий аккаунт; токен не передавать третьим лицам. Для TestFlight/App Store production, debug development profile sandbox. Конфигурация APNs обязательна на сервере.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "deviceToken": {
            "type": "string",
            "pattern": "^(?:[0-9a-fA-F]{2}){32,256}$"
          },
          "environment": {
            "type": "string",
            "enum": [
              "production",
              "sandbox"
            ]
          },
          "deviceLabel": {
            "type": "string",
            "maxLength": 100
          }
        },
        "required": [
          "deviceToken",
          "environment"
        ]
      }
    }
  }
}

Ответы

{
  "204": {
    "description": "Успешно, без тела ответа."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "409": {
    "$ref": "#/components/responses/Conflict"
  },
  "503": {
    "$ref": "#/components/responses/Unavailable"
  }
}
DELETE/api/v1/push/apnsОтключить native iOS push

Только сессия

Удаляет токен только у текущего аккаунта, в обоих APNs environments.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "deviceToken": {
            "type": "string",
            "pattern": "^(?:[0-9a-fA-F]{2}){32,256}$"
          }
        },
        "required": [
          "deviceToken"
        ]
      }
    }
  }
}

Ответы

{
  "204": {
    "description": "Успешно, без тела ответа."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
GET/api/v1/usersНайти пользователя

Сессия или API-токен

Только авторизованные запросы. Телефоны других пользователей не возвращаются. Поиск ограничен по частоте.

Параметры

[
  {
    "name": "q",
    "in": "query",
    "required": true,
    "schema": {
      "type": "string",
      "minLength": 3
    },
    "description": "Точный E.164 телефон либо префикс username от 3 символов, без @."
  }
]

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/PublicUser"
          },
          "maxItems": 20
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
POST/api/v1/contacts/resolveНайти зарегистрированные контакты

Сессия или API-токен

Bulk lookup для адресной книги. Клиент отправляет только локальный ref и нормализованный телефон; имена контактов остаются на устройстве. Ответ возвращает ref + PublicUser без телефона. Только registered пользователи текущего review-сегмента, suspended исключаются; self исключается.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "contacts": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "ref": {
                  "type": "string",
                  "maxLength": 128
                },
                "phone": {
                  "type": "string",
                  "pattern": "^\\+[1-9][0-9]{7,14}$",
                  "examples": [
                    "+79991234567"
                  ]
                }
              },
              "required": [
                "ref",
                "phone"
              ]
            },
            "minItems": 1,
            "maxItems": 500
          }
        },
        "required": [
          "contacts"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/ContactResolveMatch"
          },
          "maxItems": 500
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
GET/api/v1/chatsСписок своих чатов

Сессия или API-токен

До 100 чатов, новые сверху (updatedAt DESC).

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/Chat"
          },
          "maxItems": 100
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
POST/api/v1/chatsСоздать личный или групповой чат

Сессия или API-токен

Текущий пользователь добавляется автоматически. direct: ровно один другой участник; существующий личный чат переиспользуется. group: 2–100 участников, title обязателен.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "direct",
              "group"
            ]
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "memberIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "minItems": 1,
            "maxItems": 99,
            "uniqueItems": true
          }
        },
        "required": [
          "kind",
          "memberIds"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Chat"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "201": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Chat"
        }
      }
    }
  }
}
GET/api/v1/chats/{chatId}Получить чат и актуальный состав

Сессия или API-токен

Параметры

[
  {
    "name": "chatId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Chat"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
DELETE/api/v1/chats/{chatId}Удалить чат у себя или у всех

Сессия или API-токен

scope=self скрывает чат только у текущего аккаунта, очищает unread/push и обрезает видимую историю: при повторном открытии старые сообщения и файлы не возвращаются. Новое сообщение снова покажет чат. scope=all полностью удаляет direct-чат для обоих участников; группу у всех может удалить только владелец. AI-чат не удаляется.

Параметры

[
  {
    "name": "chatId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "scope": {
            "type": "string",
            "enum": [
              "self",
              "all"
            ],
            "default": "self"
          }
        },
        "required": [
          "scope"
        ]
      }
    }
  }
}

Ответы

{
  "204": {
    "description": "Успешно, без тела ответа."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
PATCH/api/v1/chats/{chatId}Переименовать группу

Сессия или API-токен

Только владелец группы.

Параметры

[
  {
    "name": "chatId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          }
        },
        "required": [
          "title"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Chat"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
POST/api/v1/chats/{chatId}/membersДобавить участника в группу

Сессия или API-токен

Только владелец. Новый участник видит сообщения после момента вступления. После изменения состава отправители должны обновить набор ключей.

Параметры

[
  {
    "name": "chatId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "userId": {
            "type": "string",
            "format": "uuid"
          }
        },
        "required": [
          "userId"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Chat"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
DELETE/api/v1/chats/{chatId}/members/{userId}Удалить участника или выйти из группы

Сессия или API-токен

Владелец удаляет других участников; участник может выйти сам. Владелец сначала передаёт владение. Удалённый участник теряет серверный доступ, но уже сохранённые им данные отозвать невозможно.

Параметры

[
  {
    "name": "chatId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  },
  {
    "name": "userId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Chat"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
PATCH/api/v1/chats/{chatId}/ownerПередать владение группой

Сессия или API-токен

Только владелец; получатель должен быть текущим участником.

Параметры

[
  {
    "name": "chatId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "userId": {
            "type": "string",
            "format": "uuid"
          }
        },
        "required": [
          "userId"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Chat"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
GET/api/v1/chats/{chatId}/messagesИстория сообщений

Сессия или API-токен

По умолчанию последняя страница; сообщения внутри страницы упорядочены по возрастанию. История скрыта до вступления и до последнего локального удаления чата у себя. Cursor обязан принадлежать видимой части чата.

Параметры

[
  {
    "name": "chatId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  },
  {
    "name": "before",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string",
      "format": "uuid"
    },
    "description": "UUID сообщения из этого чата; пагинация назад."
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "schema": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 50
    },
    "description": ""
  }
]

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "messages": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Message"
              }
            },
            "hasMore": {
              "type": "boolean"
            }
          },
          "required": [
            "messages",
            "hasMore"
          ]
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
POST/api/v1/chats/{chatId}/messagesОтправить зашифрованное сообщение

Сессия или API-токен

Идемпотентность по (chatId, senderId, clientId). Ключи envelope.keys точно соответствуют текущим участникам. Сервер проверяет подпись и принадлежность вложений отправителю/чату. При конфликте состава обновите чат и повторно зашифруйте сообщение.

Параметры

[
  {
    "name": "chatId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "clientId": {
            "type": "string",
            "format": "uuid"
          },
          "envelope": {
            "$ref": "#/components/schemas/Envelope"
          },
          "attachmentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "maxItems": 10,
            "uniqueItems": true
          }
        },
        "required": [
          "clientId",
          "envelope",
          "attachmentIds"
        ]
      },
      "examples": {
        "encryptedMessage": {
          "summary": "Real encryption and signature; illustrative account IDs, use your own keys.",
          "value": {
            "clientId": "a2480949-214c-42f7-89f7-96a73c25fef1",
            "envelope": {
              "version": 1,
              "ciphertext": "V5UxRNH65m0xPA5yXUGw_eWo0aLrjzIml84mGKa0x3Pwkr-kRrsOwNp1b_Y6K93R3ekPJQgSndTfym0sVAac2R3G0tj2NRKZtKl5VnVJuKMizv7rDNd4AINWH4fdvD79VroSyWvmbD8",
              "iv": "j5IJPyjMtzX3ALZa",
              "keys": {
                "0f52009a-c706-4380-9d8e-1ec81861fe0c": "BWKH4pTfgu8uqwnP4ZEILXIjbI0bglc_qyfeVeGSW9odjtKT1IXML_NKfJzXXQqPB-x50yt4wehqLKFXibYMWqpZKWJUGM89CD1PO-EZM9cXiqDe2x6IhfIVb7EYZBvwQTGYGDT65gBw2RHfzrWidnsRv9SVWS7xle5vPFThQ4oX1DdZYNA47hTxNoY03_GPNVPvsRGBtLoR5Q2KbFA2gBYVqV3Jwe6p3yAagzPepbttvHwy7O1kVPdNOCl9gJYuF7J7kj8IeMsU48o3lf0Msvv-PRSry_httTlQGPZmpACAHU3vy-f4JMDMfKz8uK9Oh1kH7tNkMmkKWrQUmdBMam4ZGVQdOiyxxn5u7w-eOsNtuIRIkQ1cOizoz4q19xma_unjoBl2ssnhbPYPKBmvqANBpYFT1rSnBnhq250NDwsZoTOT4I7p39rUxHCzdrIIYq18ZgTVTsj-qb2AYg08GthyiHcKeqeAoGvZ2ryUFng7q7O907KOqiT9VMmTEMni",
                "baf3a165-a631-4214-8a53-c356105db635": "VO7DWZ6_Mm1FDf2eGpKwPoCA50TTrcy0JH559MhZViFG9rlZ312TV31Pw0nRSBb-ACOgRlhvyycXgyOhghofALIE5NkRF3TCmMKtBqr3ofg46UjOrUDMKQa9S2bJeJNgoCsCENAtTYBJeErIQ3UAbGsweuqUtDhzJRC4uKq5hnxFNvGM_lowg-DVNTVoTJk_Ru6ZBxE4yWm20WQeK90HunLqiYV14hqDkJGY_MF0s9O6CKgDWfqQj3ksF2QQlrf12oSiCCH2tc8krZatfJDmpkoxEROVYxhU06jsm9z-BWofji4bTY2WYtmevJEJzr39_dFL-wpj8GFSjILKCLggvQznTqXAru5fTSO5taUNfrLICVfc1s5qRQsiLbetm_f9NUJRe-ZmLz072YbYgifrkJoFdzI4gqosddAft85H5uJcVap5JQXNKQ682hPf19GwM6kl6OVrfkv0X221rLvmswlVC08525ZCAriplyopEdX1XczARc9EXTRB2BZSsS-h"
              },
              "signature": "pl7HY86koBnCAujtZ09RGbf2R_2W0Tyzn0TuKOwJlgoQbNcexFCowuwOxbKaVTMU7ZVcW-yddcr1PPICEW_GmQ"
            },
            "attachmentIds": []
          }
        }
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Message"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "409": {
    "$ref": "#/components/responses/Conflict"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "201": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Message"
        }
      }
    }
  }
}
PATCH/api/v1/chats/{chatId}/messages/{messageId}Изменить своё сообщение

Сессия или API-токен

Только отправитель. При изменении содержимого сохраняется исходный clientId, генерируются свежие ключ/IV/подпись для актуального состава. Повтор того же envelope и списка вложений возвращает сохранённую версию без изменения editedAt.

Параметры

[
  {
    "name": "chatId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  },
  {
    "name": "messageId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "envelope": {
            "$ref": "#/components/schemas/Envelope"
          },
          "attachmentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "maxItems": 10,
            "uniqueItems": true
          }
        },
        "required": [
          "envelope",
          "attachmentIds"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Message"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "409": {
    "$ref": "#/components/responses/Conflict"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
DELETE/api/v1/chats/{chatId}/messages/{messageId}Удалить своё сообщение

Сессия или API-токен

Только отправитель. В истории остаётся отметка удаления. Уже сохранённые получателями копии отозвать невозможно.

Параметры

[
  {
    "name": "chatId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  },
  {
    "name": "messageId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Ответы

{
  "204": {
    "description": "Успешно, без тела ответа."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
POST/api/v1/chats/{chatId}/readОтметить сообщения прочитанными

Сессия или API-токен

Указатель прочтения только продвигается вперёд. Сообщение должно быть доступно участнику.

Параметры

[
  {
    "name": "chatId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "messageId": {
            "type": "string",
            "format": "uuid"
          }
        },
        "required": [
          "messageId"
        ]
      }
    }
  }
}

Ответы

{
  "204": {
    "description": "Успешно, без тела ответа."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
POST/api/v1/chats/{chatId}/filesЗагрузить зашифрованный файл

Сессия или API-токен

Тело — AES-256-GCM ciphertext с тегом, application/octet-stream. До 50 MiB исходного файла + 16 байт GCM tag. Без имени, MIME и других открытых метаданных. Свяжите id с сообщением через attachmentIds. Лимит загрузки на пользователя — 2 GiB за последние 24 часа.

Параметры

[
  {
    "name": "chatId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Тело запроса

{
  "required": true,
  "content": {
    "application/octet-stream": {
      "schema": {
        "type": "string",
        "format": "binary",
        "maxLength": 52428816
      }
    }
  }
}

Ответы

{
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "413": {
    "$ref": "#/components/responses/BadRequest"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "201": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "id": {
              "type": "string",
              "format": "uuid"
            },
            "size": {
              "type": "integer",
              "minimum": 16,
              "maximum": 52428816
            }
          },
          "required": [
            "id",
            "size"
          ]
        }
      }
    }
  }
}
GET/api/v1/files/{fileId}Скачать зашифрованный файл

Сессия или API-токен

Получатель имеет доступ только к файлам доступных ему сообщений после вступления и после последнего локального удаления чата у себя. Загрузивший пользователь может скачать своё ещё не прикреплённое вложение, пока чат не скрыт у него локальным удалением. Исходное имя файла не возвращается.

Параметры

[
  {
    "name": "fileId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/octet-stream": {
        "schema": {
          "type": "string",
          "format": "binary"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
GET/api/v1/eventsПоток изменений SSE

Сессия или API-токен

Используйте fetch с Authorization, не EventSource с токеном в URL. Событие change содержит Event. Heartbeat каждые 20 секунд. После переподключения перечитайте данные через REST; SSE не гарантирует воспроизведение пропущенного. Отзыв/истечение сессии закрывает доступ.

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "text/event-stream": {
        "schema": {
          "type": "string"
        },
        "example": "event: change\ndata: {\"type\":\"message\",\"chatId\":\"8ed92c28-397f-4b1f-8016-b7b484e809ef\"}\n\n"
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
PUT/api/v1/push/subscriptionПодписать устройство на push

Сессия или API-токен

HTTPS endpoint разрешённого публичного push-провайдера; upsert по пользователю и endpoint. Уведомление содержит только общий текст и ссылку на чат. Содержимое сообщений не передаётся push-провайдеру.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/PushSubscription"
      }
    }
  }
}

Ответы

{
  "204": {
    "description": "Успешно, без тела ответа."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
DELETE/api/v1/push/subscriptionУдалить push-подписку

Сессия или API-токен

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "endpoint": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "endpoint"
        ]
      }
    }
  }
}

Ответы

{
  "204": {
    "description": "Успешно, без тела ответа."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
GET/api/v1/tokensСписок интеграционных токенов

Только сессия

Требует пользовательскую сессию. Значения ранее выданных токенов не возвращаются.

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/Token"
          }
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
POST/api/v1/tokensВыпустить интеграционный токен

Только сессия

Только пользовательская сессия. read разрешает чтение, write — изменения; для обоих видов запросов выдавайте оба scope. Токен не предоставляет приватные ключи и не обходит E2EE. Его значение возвращается один раз.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read",
                "write"
              ]
            },
            "minItems": 1,
            "maxItems": 2,
            "uniqueItems": true
          },
          "expiresInDays": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365
          }
        },
        "required": [
          "name",
          "scopes",
          "expiresInDays"
        ]
      }
    }
  }
}

Ответы

{
  "201": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CreatedToken"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
DELETE/api/v1/tokens/{tokenId}Отозвать интеграционный токен

Только сессия

Параметры

[
  {
    "name": "tokenId",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "format": "uuid"
    }
  }
]

Ответы

{
  "204": {
    "description": "Успешно, без тела ответа."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
POST/api/v1/ai/messagesОтправить сообщение закреплённому AI-ассистенту

Сессия или API-токен

Только зарегистрированная сессия или API-токен с write scope. Сервер создаёт закреплённый чат kind=ai, сохраняет текст этого AI-чата в plaintext и отправляет историю этого AI-чата в OpenAI через настроенный SSH/SOCKS proxy. Обычные E2EE-чаты и файлы ассистенту недоступны.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/AIMessageRequest"
      }
    }
  }
}

Ответы

{
  "201": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/AIMessageResult"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "503": {
    "$ref": "#/components/responses/Unavailable"
  }
}
GET/api/v1/livechat/siteПолучить модуль онлайн-консультаций

Сессия или API-токен

Возвращает скрипт и HTML-код для установки виджета на сайт. Требует зарегистрированный аккаунт; гостевые посетители не допускаются. API-токену нужен scope read.

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/LiveChatSite"
        }
      }
    }
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
POST/api/v1/livechat/siteСоздать или обновить модуль онлайн-консультаций

Только сессия

Один активный модуль на аккаунт оператора. Виджет создаёт зашифрованные чаты с этим аккаунтом, а ответы оператор пишет в обычном STIX Chat. API-токену нужен scope write.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          }
        },
        "required": [
          "name"
        ]
      }
    }
  }
}

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/LiveChatSite"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}
POST/api/v1/livechat/sessionsОткрыть сессию посетителя сайта

Публичный метод

Публичный endpoint для iframe-виджета. При первом обращении создаёт guest-аккаунт visitor, групповой чат с владельцем модуля и возвращает visitorToken для этого браузера. При повторном обращении с visitorToken выдаёт новую сессию к тому же чату. Сервер не получает открытый текст сообщений.

Тело запроса

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/LiveChatSessionRequest"
      }
    }
  }
}

Ответы

{
  "201": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/LiveChatSession"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/LiveChatSession"
        }
      }
    }
  }
}
GET/healthzПроверка процесса

Публичный метод

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "additionalProperties": true
        }
      }
    }
  },
  "503": {
    "$ref": "#/components/responses/Unavailable"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  }
}
GET/readyzГотовность и соединение с БД

Публичный метод

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "additionalProperties": true
        }
      }
    }
  },
  "503": {
    "$ref": "#/components/responses/Unavailable"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  }
}
GET/openapi.yamlСпецификация OpenAPI

Публичный метод

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "application/yaml": {
        "schema": {
          "type": "string"
        }
      }
    }
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  }
}
GET/api-docsЛокальная документация API

Публичный метод

Ответы

{
  "200": {
    "description": "Успешно.",
    "content": {
      "text/html": {
        "schema": {
          "type": "string"
        }
      }
    }
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  }
}

Схемы данных

Error
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "error": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "code": {
          "type": "string"
        },
        "message": {
          "type": "string",
          "description": "Сообщение на русском языке."
        }
      },
      "required": [
        "code",
        "message"
      ]
    }
  },
  "required": [
    "error"
  ]
}
RSAPublicKey
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "kty": {
      "type": "string",
      "const": "RSA"
    },
    "n": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]+$"
    },
    "e": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]+$"
    },
    "alg": {
      "type": "string"
    },
    "ext": {
      "type": "boolean"
    },
    "key_ops": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "kty",
    "n",
    "e"
  ],
  "description": "RSA-OAEP SHA-256, 3072 bits; public JWK only. Private key fields are rejected."
}
SigningPublicKey
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "kty": {
      "type": "string",
      "const": "EC"
    },
    "crv": {
      "type": "string",
      "const": "P-256"
    },
    "x": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]+$"
    },
    "y": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]+$"
    },
    "alg": {
      "type": "string"
    },
    "ext": {
      "type": "boolean"
    },
    "key_ops": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "kty",
    "crv",
    "x",
    "y"
  ],
  "description": "ECDSA P-256 SHA-256 public JWK. Private key fields are rejected."
}
PublicKeys
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "encryption": {
      "$ref": "#/components/schemas/RSAPublicKey"
    },
    "signing": {
      "$ref": "#/components/schemas/SigningPublicKey"
    }
  },
  "required": [
    "encryption",
    "signing"
  ]
}
KeyBackup
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "version": {
      "type": "integer",
      "const": 1
    },
    "iv": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]+$"
    },
    "ciphertext": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]+$"
    }
  },
  "required": [
    "version",
    "iv",
    "ciphertext"
  ],
  "description": "Private identity encrypted with AES-256-GCM and a random recovery key. Recovery key never reaches the server."
}
PublicUser
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "username": {
      "type": "string",
      "minLength": 3,
      "maxLength": 32,
      "pattern": "^[a-zA-Z0-9_]+$"
    },
    "name": {
      "type": "string",
      "maxLength": 100
    },
    "avatarUrl": {
      "type": "string",
      "description": "Optional public avatar as a small data:image/png/jpeg/webp URL."
    },
    "publicKey": {
      "$ref": "#/components/schemas/PublicKeys"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "id",
    "username",
    "name",
    "publicKey",
    "createdAt"
  ],
  "description": "Phone is excluded from all public user responses."
}
User
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "username": {
      "type": "string",
      "minLength": 3,
      "maxLength": 32,
      "pattern": "^[a-zA-Z0-9_]+$"
    },
    "name": {
      "type": "string",
      "maxLength": 100
    },
    "avatarUrl": {
      "type": "string",
      "description": "Optional public avatar as a small data:image/png/jpeg/webp URL."
    },
    "publicKey": {
      "$ref": "#/components/schemas/PublicKeys"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "phone": {
      "type": "string",
      "pattern": "^\\+[1-9][0-9]{7,14}$",
      "examples": [
        "+79991234567"
      ]
    },
    "keyBackup": {
      "$ref": "#/components/schemas/KeyBackup"
    }
  },
  "required": [
    "id",
    "username",
    "name",
    "publicKey",
    "createdAt",
    "phone",
    "keyBackup"
  ]
}
Member
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "username": {
      "type": "string",
      "minLength": 3,
      "maxLength": 32,
      "pattern": "^[a-zA-Z0-9_]+$"
    },
    "name": {
      "type": "string",
      "maxLength": 100
    },
    "avatarUrl": {
      "type": "string",
      "description": "Optional public avatar as a small data:image/png/jpeg/webp URL."
    },
    "publicKey": {
      "$ref": "#/components/schemas/PublicKeys"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "role": {
      "type": "string",
      "enum": [
        "owner",
        "member"
      ]
    },
    "joinedAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "id",
    "username",
    "name",
    "publicKey",
    "createdAt",
    "role",
    "joinedAt"
  ]
}
Envelope
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "version": {
      "type": "integer",
      "const": 1
    },
    "ciphertext": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]+$"
    },
    "iv": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]+$"
    },
    "keys": {
      "type": "object",
      "propertyNames": {
        "type": "string",
        "format": "uuid"
      },
      "additionalProperties": {
        "type": "string",
        "pattern": "^[A-Za-z0-9_-]+$"
      },
      "minProperties": 1,
      "maxProperties": 100
    },
    "signature": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]+$"
    }
  },
  "required": [
    "version",
    "ciphertext",
    "iv",
    "keys",
    "signature"
  ],
  "description": "Fresh AES-256-GCM key per message, wrapped with each current member's RSA-OAEP key. All binary values use unpadded base64url. Signature and AAD are specified in SECURITY.md. Maximum serialized envelope is 128 KiB."
}
Message
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "chatId": {
      "type": "string",
      "format": "uuid"
    },
    "senderId": {
      "type": "string",
      "format": "uuid"
    },
    "sender": {
      "$ref": "#/components/schemas/PublicUser"
    },
    "clientId": {
      "type": "string",
      "format": "uuid"
    },
    "envelope": {
      "oneOf": [
        {
          "$ref": "#/components/schemas/Envelope"
        },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {},
          "required": [],
          "description": "Empty object for a deleted or AI plaintext message."
        }
      ]
    },
    "attachmentIds": {
      "type": "array",
      "items": {
        "type": "string",
        "format": "uuid"
      },
      "maxItems": 10,
      "uniqueItems": true
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "editedAt": {
      "type": "string",
      "format": "date-time"
    },
    "deletedAt": {
      "type": "string",
      "format": "date-time"
    },
    "plaintext": {
      "type": "string",
      "description": "Only present in the dedicated AI assistant chat; ordinary chats remain E2EE."
    },
    "deliveryStatus": {
      "type": "string",
      "enum": [
        "sent",
        "delivered",
        "read"
      ],
      "description": "Present for messages sent by the current user. Delivered means accepted by the server and available to active recipients; read means every active recipient for that message has read up to it."
    }
  },
  "required": [
    "id",
    "chatId",
    "senderId",
    "sender",
    "clientId",
    "envelope",
    "attachmentIds",
    "createdAt"
  ],
  "description": "Sender identity accompanies history so signatures remain verifiable after the sender leaves a group. Deletion retains a tombstone."
}
Chat
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "kind": {
      "type": "string",
      "enum": [
        "direct",
        "group",
        "ai"
      ]
    },
    "title": {
      "type": "string",
      "maxLength": 100
    },
    "members": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Member"
      },
      "minItems": 1,
      "maxItems": 100
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time"
    },
    "unreadCount": {
      "type": "integer",
      "minimum": 0
    },
    "pinnedAt": {
      "type": "string",
      "format": "date-time"
    },
    "lastMessage": {
      "$ref": "#/components/schemas/Message"
    }
  },
  "required": [
    "id",
    "kind",
    "title",
    "members",
    "createdAt",
    "updatedAt",
    "unreadCount"
  ]
}
Attachment
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string"
    },
    "mime": {
      "type": "string"
    },
    "size": {
      "type": "integer",
      "minimum": 0,
      "maximum": 52428800
    },
    "kind": {
      "type": "string",
      "enum": [
        "image",
        "video",
        "pdf",
        "stl",
        "audio",
        "text",
        "code",
        "table",
        "svg",
        "file"
      ]
    },
    "key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]+$"
    },
    "iv": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]+$"
    },
    "duration": {
      "type": "number",
      "minimum": 0
    }
  },
  "required": [
    "id",
    "name",
    "mime",
    "size",
    "kind",
    "key",
    "iv"
  ],
  "description": "CLIENT-ONLY decrypted attachment descriptor inside MessageContent. Filename, MIME type, plaintext size and decryption material never go to plaintext API fields."
}
MessageContent
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "text": {
      "type": "string"
    },
    "attachments": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Attachment"
      }
    },
    "replyTo": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "id": {
          "type": "string",
          "format": "uuid"
        },
        "text": {
          "type": "string"
        },
        "senderName": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "text",
        "senderName"
      ]
    }
  },
  "required": [
    "text",
    "attachments"
  ],
  "description": "CLIENT-ONLY plaintext schema, encrypted into Envelope.ciphertext before sending."
}
Config
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "smsConfigured": {
      "type": "boolean"
    },
    "pushPublicKey": {
      "type": "string"
    },
    "apnsConfigured": {
      "type": "boolean"
    },
    "maxFileBytes": {
      "type": "integer",
      "examples": [
        52428800
      ]
    },
    "environment": {
      "type": "string",
      "enum": [
        "development",
        "production",
        "test"
      ]
    }
  },
  "required": [
    "smsConfigured",
    "pushPublicKey",
    "apnsConfigured",
    "maxFileBytes",
    "environment"
  ]
}
PublicStats
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "registeredAccounts": {
      "type": "integer",
      "minimum": 0
    },
    "generatedAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "registeredAccounts",
    "generatedAt"
  ],
  "description": "Public aggregate counter. Excludes system, visitor, suspended and App Review test accounts."
}
Challenge
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "challengeId": {
      "type": "string",
      "format": "uuid"
    },
    "retryAfter": {
      "type": "integer",
      "examples": [
        60
      ]
    },
    "expiresIn": {
      "type": "integer",
      "examples": [
        300
      ]
    }
  },
  "required": [
    "challengeId",
    "retryAfter",
    "expiresIn"
  ]
}
StartLoginResult
{
  "oneOf": [
    {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "registered": {
          "type": "boolean",
          "const": false
        }
      },
      "required": [
        "registered"
      ]
    },
    {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "registered": {
          "type": "boolean",
          "const": true
        },
        "challengeId": {
          "type": "string",
          "format": "uuid"
        },
        "challenge": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]+$"
        },
        "user": {
          "$ref": "#/components/schemas/User"
        }
      },
      "required": [
        "registered",
        "challengeId",
        "challenge",
        "user"
      ]
    }
  ]
}
Session
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "token": {
      "type": "string",
      "description": "Opaque bearer token; save securely, never put in URL."
    },
    "user": {
      "$ref": "#/components/schemas/User"
    }
  },
  "required": [
    "token",
    "user"
  ]
}
RegistrationRequired
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "registrationToken": {
      "type": "string",
      "description": "Single-use token expiring after 10 minutes."
    }
  },
  "required": [
    "registrationToken"
  ]
}
Token
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string",
      "maxLength": 100
    },
    "scopes": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "read",
          "write"
        ]
      },
      "minItems": 1,
      "maxItems": 2,
      "uniqueItems": true
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time"
    },
    "lastUsedAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "id",
    "name",
    "scopes",
    "createdAt",
    "expiresAt"
  ]
}
CreatedToken
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "token": {
      "type": "string",
      "description": "Returned once; store securely."
    },
    "name": {
      "type": "string"
    },
    "scopes": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "read",
          "write"
        ]
      },
      "minItems": 1,
      "maxItems": 2,
      "uniqueItems": true
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "id",
    "token",
    "name",
    "scopes",
    "expiresAt"
  ]
}
AIMessageRequest
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "chatId": {
      "type": "string",
      "format": "uuid"
    },
    "text": {
      "type": "string",
      "minLength": 1,
      "maxLength": 4000
    }
  },
  "required": [
    "chatId",
    "text"
  ]
}
AIMessageResult
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "userMessage": {
      "$ref": "#/components/schemas/Message"
    },
    "assistantMessage": {
      "$ref": "#/components/schemas/Message"
    }
  },
  "required": [
    "userMessage",
    "assistantMessage"
  ]
}
ContactResolveMatch
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "ref": {
      "type": "string",
      "maxLength": 128
    },
    "user": {
      "$ref": "#/components/schemas/PublicUser"
    }
  },
  "required": [
    "ref",
    "user"
  ],
  "description": "Contact phone is never returned. The caller maps ref back to its local address book entry."
}
LiveChatSite
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string",
      "maxLength": 80
    },
    "scriptUrl": {
      "type": "string",
      "format": "uri"
    },
    "embedCode": {
      "type": "string"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "id",
    "name",
    "scriptUrl",
    "embedCode",
    "createdAt"
  ],
  "description": "Embeddable live consultation module owned by a registered operator account."
}
LiveChatSessionRequest
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "siteId": {
      "type": "string",
      "format": "uuid"
    },
    "visitorToken": {
      "type": "string"
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80
    },
    "pageUrl": {
      "type": "string",
      "format": "uri",
      "maxLength": 500
    },
    "publicKey": {
      "$ref": "#/components/schemas/PublicKeys"
    },
    "keyBackup": {
      "$ref": "#/components/schemas/KeyBackup"
    }
  },
  "required": [
    "siteId",
    "name"
  ],
  "description": "visitorToken resumes an existing browser visitor. New visitors must also send publicKey and keyBackup generated in the widget."
}
LiveChatSession
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "token": {
      "type": "string",
      "description": "30-day visitor bearer token for this browser session."
    },
    "visitorToken": {
      "type": "string",
      "description": "Persistent browser token returned only when the visitor is created."
    },
    "user": {
      "$ref": "#/components/schemas/PublicUser"
    },
    "chat": {
      "$ref": "#/components/schemas/Chat"
    }
  },
  "required": [
    "token",
    "user",
    "chat"
  ]
}
PushSubscription
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "endpoint": {
      "type": "string",
      "format": "uri",
      "pattern": "^https://"
    },
    "keys": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "p256dh": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]+$"
        },
        "auth": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]+$"
        }
      },
      "required": [
        "p256dh",
        "auth"
      ]
    },
    "deviceLabel": {
      "type": "string",
      "maxLength": 100
    },
    "platform": {
      "type": "string",
      "maxLength": 40
    },
    "isIOS": {
      "type": "boolean"
    },
    "isStandalone": {
      "type": "boolean"
    }
  },
  "required": [
    "endpoint",
    "keys"
  ]
}
Event
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "chatId": {
      "type": "string",
      "format": "uuid"
    },
    "type": {
      "type": "string",
      "enum": [
        "message",
        "chat",
        "read"
      ]
    }
  },
  "required": [
    "type"
  ]
}