HTTP API
Публичный HTTP-метод для внешних систем — отправка текста в существующий диалог Amobit Chats. Чтение диалогов и истории для AI-клиентов идёт через MCP, настройка каналов и интеграции — через кабинет.
Аутентификация
Токен выпускает владелец или администратор компании: Профиль → Интеграции → API / Webhook → Создать токен. Токен показывается один раз, начинается с amc_ и передаётся в заголовке:
Authorization: Bearer amc_ВАШ_ТОКЕН
Эквивалентная форма, если ваш HTTP-клиент не умеет Authorization:
X-Amobit-Chats-Token: amc_ВАШ_ТОКЕН
| Свойство токена | Значение |
|---|---|
| Область действия | Одна компания (workspace) — тот, для которого выпущен токен. |
| Права | Отправка текста в существующие диалоги этой компании. Чтения истории и управления каналами нет. Токен принимается только эндпоинтом /inbound/messages/; настройки интеграции и журнал доставок требуют сессию пользователя. |
| Срок действия | Срок зашит в токен: по умолчанию 365 дней (настраивается на стороне Amobit). Просроченный токен отклоняется с 401; при выпуске API возвращает expires_at, а настройки интеграции — api_token_expires_at. |
| Хранение на стороне Amobit | SHA-256-хеш; полный токен в базе не хранится. |
| Замена | Новый выпуск немедленно отключает предыдущий токен и сообщает новый срок действия. |
| Отзыв | Замена токена, отключение интеграции или удаление интеграции в кабинете. |
| Секрет подписи | Отдельный секрет для HMAC вебхуков, минимум 16 символов. Токен и секрет — разные значения. |
Токены, выпущенные до появления срока действия. Старый формат amc_… без встроенного срока продолжает работать до замены. Перевыпустите его в кабинете, чтобы получить ограниченный срок и expires_at в ответе.
Не передавайте токен в браузер. Это серверный секрет: вызывайте API только с вашего backend. Токен не должен попадать в Git, логи, URL, аналитику и сообщения в мессенджерах.
Отправить сообщение
Отправляет текст от имени компании в уже существующий диалог. Метод не создаёт новые диалоги и не отправляет медиа.
Заголовки
| Заголовок | Обязательный | Описание |
|---|---|---|
Authorization | Да* | Bearer amc_… |
Content-Type | Да | application/json |
X-Amobit-Chats-Token | Да* | Альтернатива Authorization, если передать токен в заголовке. |
* Достаточно одного из двух заголовков.
Тело запроса
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
thread_id | UUID | Условно | ID диалога из webhook. Предпочтительный способ адресации. |
channel_profile_id | UUID | Условно | ID канала; используется вместе с external_chat_id. |
channel | string, ≤ 50 | Условно | Тип канала (whatsapp, telegram, max…). Устаревшая пара к external_chat_id. |
external_chat_id | string, ≤ 255 | Условно | ID чата на стороне канала, например 79990001122@c.us. |
text | string, 1–4000 | Да | Текст сообщения. Пробелы по краям обрезаются. |
idempotency_key | string, ≤ 255 | Нет | Ключ повтора: латиница, цифры, . _ : -. Начинается с буквы или цифры. Настоятельно рекомендуется. |
metadata | object | Нет | Служебные данные вашей системы. Сохраняются с намерением отправки для диагностики и не влияют на доставку. |
Как выбирается диалог
thread_id— точный ID диалога в этой компании.channel_profile_id+external_chat_id— поиск в пределах указанного канала.channel+external_chat_id— поиск по типу канала без указания профиля (устаревший вариант, для совместимости).
Если подходящих диалогов несколько, API отвечает 409 вместо выбора «наугад»: отправить сообщение не тому адресату хуже, чем вернуть ошибку. Используйте thread_id из webhook — он однозначен.
Идемпотентность
- Ключ уникален в пределах компании и диалога.
- Повтор с тем же ключом и тем же текстом возвращает
200и существующее сообщение — новый дубль не создаётся. - Тот же ключ с другим текстом возвращает
409: ключ уже привязан к другому запросу. - Проверяется только текст:
metadataв сравнении не участвует. Тот же ключ, тот же текст и новыеmetadataвернут первое сообщение, новые данные не применятся. - Без ключа каждый запрос создаёт новое сообщение — при сетевом сбое возможен дубль.
Неизвестные поля игнорируются. Если в имени поля опечатка, ошибки 400 не будет: поле молча отбросится. Передавайте только поля из таблицы выше и проверяйте ответ.
Успешный ответ
201 Created — сообщение записано и принято к отправке. Для мессенджеров это значит, что провайдер принял запрос; для веб-чата провайдера нет — сообщение сразу доставлено в диалог. Тело ответа — объект сообщения (фрагмент):
{
"id": "f09de45d-0000-4000-9000-000000000003",
"thread_id": "9fd48a99-f5f3-4c3d-8ca1-1f0e047d5b25",
"channel": "whatsapp",
"direction": "outgoing",
"message_type": "text",
"text": "Ответ из CRM",
"preview_text": "Ответ из CRM",
"delivery_status": "pending",
"sent_at": "2026-10-20T12:00:00+00:00",
"provider_transport_id": "",
"attachments": []
}
delivery_status отражает состояние на момент ответа: pending, sent, delivered, read или failed. 201 означает, что запрос принят, а не что сообщение доставлено получателю.
Ответ — объект сообщения интерфейса, а не отдельный публичный DTO. В нём могут быть служебные поля UI и ссылки на медиа, доступные только в сессии пользователя. Для интеграции опирайтесь на id, thread_id, direction, text, delivery_status, sent_at и attachments[].id/kind/file_name/mime_type/size_bytes; остальные поля не считайте стабильным контрактом.
Коды ответов
| Код | Когда возвращается | Что делать |
|---|---|---|
201 | Сообщение записано и принято к отправке: для мессенджеров — провайдер принял запрос, для веб-чата — доставлено в диалог. | Сохранить id сообщения. |
200 | Повтор по idempotency_key: возвращается существующее сообщение. Также возможен, когда сообщение записано, но подтверждение провайдера ещё не получено. | Проверить delivery_status и id. |
202 | Провайдер принял отправку, но локальная сверка/сохранение не завершены. Повторная отправка провайдеру не выполняется. | Повторить тот же запрос с тем же ключом, чтобы получить текущее состояние; сверить историю диалога. |
400 | Ошибка валидации: нет text, слишком длинный текст, неверный ключ, не передан адресат. | Исправить тело; текст ошибки — объект с полями. |
401 | Токен отсутствует, отозван или неверен. | Выпустить новый токен в кабинете. |
402 | Отправка заблокирована тарифом или подпиской канала. | Проверить оплату канала в кабинете; в ответе detail и billing_status. |
404 | Диалог не найден в этой компании. | Проверить thread_id / external_chat_id. |
409 | Неоднозначный диалог; канал не подключён для исходящих; группа исключена настройкой канала; ключ идемпотентности занят другим текстом. | Разобрать detail; для ключа — использовать новый ключ только для нового сообщения. |
502 | Провайдер не подтвердил отправку; исход неизвестен. | Повторить с тем же idempotency_key; сохранить error_id для поддержки. |
500 | Внутренняя ошибка. | Повторить с тем же ключом; если повторяется — в поддержку. |
Ограничения
- Только текст. Медиа, геолокация, контакты и реакции через этот API не отправляются.
- Только существующие диалоги. Начать переписку с новым контактом нельзя.
- Текст ≤ 4000 символов, ключ идемпотентности ≤ 255.
- Отдельной квоты на токен и лимита частоты на этом эндпоинте нет. Действуют ограничения канала и провайдера; при массовых рассылках используйте кампании в кабинете, а не этот API.
- Ответ через API не останавливает AI-агента. В отличие от ответа оператора в интерфейсе, сообщение из внешней системы не переводит диалог в режим «человек отвечает». Если на канале работает AI-агент, приостановите его на своей стороне, иначе клиент получит два ответа.
- Статусы доставки не приходят отдельным событием. Фиксируйте
delivery_statusиз ответа и событияmessage.created; отдельного вебхука статуса пока нет.
Write-first намеренно недоступен. Новый диалог с незнакомым номером затрагивает лимиты провайдера, согласие получателя и антиспам-правила. Для таких сценариев нужен отдельный согласованный процесс, а не публичный API.
Примеры
curl
curl -X POST https://chats.amobit.io/api/v1/external-integrations/inbound/messages/ \
-H "Authorization: Bearer amc_ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{
"thread_id": "9fd48a99-f5f3-4c3d-8ca1-1f0e047d5b25",
"text": "Здравствуйте! Отвечаем из CRM.",
"idempotency_key": "crm-outbox-1001",
"metadata": {"lead_id": "42"}
}'
Node.js (fetch, Node 18+)
const res = await fetch(
"https://chats.amobit.io/api/v1/external-integrations/inbound/messages/",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AMOBIT_CHATS_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
thread_id: threadId,
text,
idempotency_key: `crm-outbox-${outboxId}`,
}),
},
);
const data = await res.json();
if (res.status === 201 || res.status === 200) return data;
if ([202, 502, 500].includes(res.status)) {
// Повторить с ТЕМ ЖЕ idempotency_key, не создавать новый.
throw new RetryableError(data.detail || "retry");
}
throw new PermanentError(data);
PHP (curl)
$payload = json_encode([
'thread_id' => $threadId,
'text' => $text,
'idempotency_key' => 'crm-outbox-' . $outboxId,
], JSON_UNESCAPED_UNICODE);
$ch = curl_init('https://chats.amobit.io/api/v1/external-integrations/inbound/messages/');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('AMOBIT_CHATS_TOKEN'),
'Content-Type: application/json',
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 20,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
Служебные эндпоинты кабинета
Настройки интеграции и выпуск токена использует сам кабинет. Журнал доставок доступен только этим API: в карточке интеграции он пока не отображается. Все методы требуют сессию Amobit ID и CSRF-токен, поэтому не предназначены для вызова из внешней системы. Список приведён для полноты контракта и для поддержки:
| Метод | Путь | Назначение |
|---|---|---|
GET | /api/v1/external-integrations/workspaces/{workspace_slug}/ | Текущие настройки интеграции. |
PATCH | /api/v1/external-integrations/workspaces/{workspace_slug}/ | Название, включение, webhook-URL, секрет, список событий. |
DELETE | /api/v1/external-integrations/workspaces/{workspace_slug}/ | Отключить интеграцию, очистить URL, секрет и токен. |
POST | /api/v1/external-integrations/workspaces/{workspace_slug}/rotate-token/ | Выпустить новый токен (показывается один раз). |
GET | /api/v1/external-integrations/workspaces/{workspace_slug}/deliveries/ | Последние 100 доставок вебхуков. |
Сессионные запросы выполняются с credentials: include и CSRF-заголовком; к ним применяются роли компании (owner/admin для изменений).
Машиночитаемый контракт
OpenAPI 3.1 описывает этот метод, схемы запроса и ответа, коды ошибок и событие вебхука. Спецификация — источник для генерации клиентов и моков; при расхождении с этой страницей приоритет у поведения API, о расхождении сообщите в поддержку.