← В кабинет

HTTP API v1 · Обзор

Программный доступ к подключённым аккаунтам мессенджеров. Один API-ключ аккаунта работает со всеми вашими профилями; конкретный профиль выбирается через profile_id. Ниже — общие правила (аутентификация, ошибки, идемпотентность, webhook'и), а полная Swagger-документация по каждой платформе живёт на отдельных страницах.

Документация по платформам

1. Быстрый старт

Создайте API-ключ в «Настройки → Интеграции», скопируйте profile_id из профиля и отправьте сообщение:

curl -X POST "https://your.host/api/v1/sync/message/send" \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"profile_id":"YOUR_PROFILE_ID","recipient":"1234567890","body":"Привет!"}'

Ответ:

{
  "status": "ok",
  "data": { "messageId": "uuid", "taskId": "uuid" }
}

2. CRM/1C интеграция

Поддерживаемый production-путь для внешних систем: профиль в кабинете, API-ключ аккаунта, webhook URL для входящих событий и импорт базы контактов из CSV/выгрузки 1C.

  1. Создайте или подключите профиль Telegram, WhatsApp или MAX.
  2. Скопируйте profile_id из профиля и API-ключ в «Настройки → Интеграции».
  3. Импортируйте базу телефонов/имён, чтобы контакты появились в адресной книге профиля.
  4. Отправляйте сообщения через sync/async endpoints нужной платформы.
  5. Подпишитесь на webhook'и messages, delivery, connection и проверяйте HMAC.

Импорт контактов из CSV

Кабинетный endpoint принимает JSON или CSV с колонками phone/Телефон, name/Имя, last_name/Фамилия и external_id/client_id для сверки с CRM/1C. Для MAX контакты сохраняются в messenger_contacts; для Telegram/WhatsApp — в общей адресной книге профиля.

curl -X POST "https://chat-api.ru/api/v1/contact-import" \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_id": "YOUR_PROFILE_ID",
    "contacts": [
      { "phone": "+79990000000", "name": "Иван", "external_id": "1C-123" }
    ]
  }'

Для ручной привязки сначала получите диалог через GET /api/v1/chats?profile_id=... и возьмите из ответа поле external_chat_id или legacy alias externalChatId. Именно это значение подставляется в URL как {chat_id}. Внутренний UUID строки диалога из кабинета сюда не передаётся. Телефон, имя и CRM/1C ID передаются только в JSON body.

Если MAX lookup вернул chat_id, это и есть внешний external_chat_id для PATCH. Если клиент написал первым, телефон можно запросить чат-ботом или привязать вручную. Ручная привязка доступна только для приватных чатов; группы и каналы вернут identity.private_chat_required.

curl -X PATCH "https://chat-api.ru/api/v1/chats/1820755/identity" \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_id": "YOUR_PROFILE_ID",
    "phone": "+79990000000",
    "contact_name": "Иван",
    "crm_external_id": "1C-123",
    "identity_status": "client"
  }'
curl -X POST "https://your.host/api/contact-import" \
  -H "Content-Type: application/json" \
  -b "session=..." \
  -d '{
    "profileId": "YOUR_PROFILE_ID",
    "csv": "external_id;Телефон;Имя;Фамилия\n1c-42;79991234567;Иван;Петров",
    "dryRun": false
  }'

Для ручной проверки адресной книги MAX тот же CSV можно превратить в файлmax-contacts-poc.vcf и импортировать его в отдельный тестовый телефон:

curl -X POST "https://your.host/api/contact-import/vcf" \
  -H "Content-Type: application/json" \
  -b "session=..." \
  -o max-contacts-poc.vcf \
  -d '{
    "csv": "external_id;phone;name;last_name\n1c-42;79991234567;Иван;Петров"
  }'

Ответ

{
  "data": {
    "profileId": "uuid",
    "provider": "max-direct",
    "imported": 1,
    "skipped": 0,
    "errors": 0,
    "rows": [
      {
        "row": 1,
        "phone": "79991234567",
        "name": "Иван",
        "lastName": "Петров",
        "sourceExternalId": "1c-42",
        "status": "imported",
        "registered": false,
        "externalId": null,
        "detectionStatus": "not_found"
      }
    ]
  }
}

MAX Direct не отдаёт полный список диалогов до синхронизации истории: чат появляется после входящего события, отправки сообщения или добавления контакта по телефону. Номер телефона MAX может быть недоступен, поэтому UI показывает phone, displayName или внешний ID — что реально отдала сессия.

3. Аутентификация

API-ключ аккаунта передаётся в HTTP-заголовке Authorization (без префикса Bearer). Для POST/PATCH/PUT передавайте profile_id в JSON body; для GET/DELETE — в query.

Ротировать или отозвать ключ можно в «Настройки → Интеграции». Старый ключ отзывается мгновенно. Все эндпоинты требуют, чтобы Postgres был основным хранилищем (SESSION_STORE=postgres).

Скоупы (на будущее)

4. Формат ошибок

Все ошибки возвращаются в едином формате:

{
  "status": "error",
  "error": {
    "code": "validation.recipient_required",
    "message": "Field 'recipient' is required",
    "details": null
  }
}
КодHTTPОписание
auth.missing_token401Не передан Authorization-заголовок
auth.missing_profile_id400Не передан profile_id
auth.profile_id_conflict400profile_id в body и query различается
auth.invalid_token401Неверный API-ключ
profile.not_found404Профиль не найден или принадлежит другому пользователю
auth.unavailable503API-режим выключен (mock store)
validation.*400Невалидное тело запроса
recipient.not_found404Чат с recipient не найден в профиле
idempotency.conflict409Тот же Idempotency-Key, но другое тело
internal500Внутренняя ошибка

5. Идемпотентность

Передайте заголовок Idempotency-Key с UUID-значением для безопасного повтора запроса. Если запрос с тем же ключом приходит повторно, возвращается кэшированный ответ. TTL — 24 часа.

curl -X POST "https://your.host/api/v1/sync/message/send" \
  -H "Authorization: ..." \
  -H "Idempotency-Key: 4a7b9e0f-1c84-4b2c-9b27-2e6e6f9e9a01" \
  -H "Content-Type: application/json" \
  -d '{"profile_id":"...","recipient":"...","body":"hi"}'

6. Эндпоинты

POST /api/contact-import

Импорт CRM/1C базы контактов в профиль из JSON или CSV. Требует кабинетную session cookie.

Тело

{
  "profileId": "uuid",
  "contacts": [{ "phone": "79991234567", "name": "Иван", "lastName": "Петров" }],
  "dryRun": false
}

POST /api/v1/contact-import

Публичный импорт CRM/1C контактов по API-ключу. Для MAX при успешном lookup сохраняет связку телефона с чатом.

Тело

{
  "profile_id": "uuid",
  "contacts": [{ "phone": "+79990000000", "name": "Иван", "external_id": "1C-123" }]
}

PATCH /api/v1/chats/{chat_id}/identity

Ручная привязка телефона, имени, CRM/1C ID и статуса к существующему приватному чату. Для групп, каналов, bot/unknown чатов возвращает identity.private_chat_required.

Path-параметр

{chat_id} — внешний идентификатор чата из поля external_chat_id ответа GET /api/v1/chats или из alias externalChatId. Для MAX это числовой MAX chat id, например 1820755; для WhatsApp — JID; для Telegram — внешний peer/chat id. Это не внутренний UUID диалога.

Тело

profile_id обязателен. Номер телефона передаётся в phone внутри body, а не в URL. Поля contact_name, crm_external_id и identity_status опциональны.

{
  "profile_id": "uuid",
  "phone": "+79990000000",
  "contact_name": "Иван",
  "crm_external_id": "1C-123",
  "identity_status": "client"
}

Ответ

{
  "status": "ok",
  "data": {
    "external_chat_id": "1820755",
    "contact_phone": "79990000000",
    "contact_name": "Иван",
    "crm_external_id": "1C-123",
    "identity_status": "client",
    "identity_source": "manual"
  }
}

POST /api/contact-import/vcf

Формирует text/vcard из такой же CSV/JSON базы для POC с отдельным MAX-телефоном. Не пишет в БД; валидные дубли пропускаются, счётчики доступны в заголовкахX-Contact-Import-*.

Тело

{
  "csv": "external_id;phone;name;last_name\n1c-42;79991234567;Иван;Петров"
}

POST /api/v1/sync/message/send

Отправить текстовое сообщение в существующий чат.

Тело

{ "profile_id": "uuid", "recipient": "1234567890", "body": "Hello" }

Ответ

{ "status": "ok", "data": { "messageId": "uuid", "taskId": "uuid" } }

recipient — это external_chat_id из существующего диалога. Чат должен быть открыт хотя бы один раз, чтобы появиться в системе. Создание чата по username — на дорожной карте.

GET /api/v1/profile/me

Информация о текущем профиле: статус авторизации, имя, телефон, юзернейм.

Ответ

{
  "status": "ok",
  "data": {
    "profileId": "uuid",
    "name": "Тест",
    "platform": "telegram",
    "externalProfileId": "f4f8d365-da2d",
    "authorized": true,
    "authorizedAt": "2026-05-07T18:09:45Z",
    "phone": "+79991234567",
    "username": "ccandybagg",
    "workerOnline": true
  }
}

GET /api/v1/chats

Постраничный список диалогов профиля. Поддерживает cursor-пагинацию и отдаёт сохранённую привязку клиента. Рекомендуемый внешний формат полей — snake_case; camelCase aliases оставлены для совместимости. Для последующей привязки клиента в PATCH /api/v1/chats/{chat_id}/identity используйте именно external_chat_id из этого ответа. Для приватных WhatsApp-чатов без сохранённой identity-привязки contact_phone дополнительно выводится из JID вида 79990000000@s.whatsapp.net, а для private @lid — из provider mapping lid → phone, если WhatsApp отдал его через contacts/history/share-phone или менеджер вручную привязал номер к LID-чату. Это fallback чтения, а не автоматическая запись в CRM identity.

Параметры

  • limit — 1..200, по умолчанию 50
  • cursor — opaque-строка из nextCursor предыдущего ответа

Ответ

{
  "status": "ok",
  "data": {
    "items": [
      {
        "externalChatId": "1234567890",
        "external_chat_id": "1234567890",
        "title": "Иван Иванов",
        "unreadCount": 3,
        "unread_count": 3,
        "lastMessageText": "Привет",
        "last_message_text": "Привет",
        "lastMessageAt": "2026-05-07T21:14:00Z",
        "last_message_at": "2026-05-07T21:14:00Z",
        "avatarUrl": null,
        "avatar_url": null,
        "contact_phone": "79990000000",
        "contact_name": "Иван",
        "crm_external_id": "1C-123",
        "identity_status": "client",
        "identity_source": "manual"
      }
    ],
    "nextCursor": "base64..."
  }
}

GET / POST / DELETE /api/v1/webhook/url

Управление webhook-подписками профиля. POST возвращает secret один раз.

POST тело

{ "url": "https://your.callback/webhook", "eventTypes": ["messages","delivery","connection"] }

POST ответ (запоминайте secret сразу!)

{
  "status": "ok",
  "data": {
    "id": "uuid",
    "url": "https://your.callback/webhook",
    "eventTypes": ["messages","delivery","connection"],
    "active": true,
    "secret": "32-байтовый base64url секрет"
  }
}

DELETE тело

{ "id": "uuid" }

7. Webhook'и

После регистрации URL мы отправляем POST-запрос на каждое выбранное событие. Подпись HMAC-SHA256 приходит в заголовке X-Wpcl-Signature: sha256=<hex>, тип события — в X-Wpcl-Event, идентификатор доставки — в X-Wpcl-Delivery.

Валидация подписи (Node.js)

import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, signatureHeader, secret) {
  const sig = signatureHeader.replace(/^sha256=/, "");
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  return timingSafeEqual(Buffer.from(sig, "hex"), Buffer.from(expected, "hex"));
}

Типы событий

Payload входящего сообщения

{
  "messageId": "uuid",
  "conversationId": "uuid",
  "provider": "telegram",
  "externalChatId": "1234567890",
  "externalMessageId": "789",
  "text": "Привет",
  "createdAt": "2026-05-07T21:14:00Z",
  "attachments": [
    {
      "id": "uuid",
      "type": "image",
      "filename": "photo.jpg",
      "mimeType": "image/jpeg",
      "sizeBytes": 153120,
      "url": "/api/messenger/attachments/uuid"
    }
  ]
}

Retry-политика

На 2xx — доставлено. На 4xx (кроме 429) — failed без ретраев. На 5xx, 429 и timeout — до 6 попыток с экспоненциальным backoff: 30s, 60s, 120s, 240s, 480s, 960s.