Як підключити асистента CallAIder до зовнішнього каналу через API

External Conversation Bridge - це спосіб підключити текстового асистента CallAIder до вашого власного каналу спілкування через API. Каналом може бути Telegram-бот, WhatsApp, Instagram, Viber, CRM-чат, чат у мобільному застосунку, власний операторський інтерфейс або будь-яка інша система, яка вміє приймати повідомлення від користувача і робити HTTP-запити на бекенд.

Головна ідея проста: CallAIder не забирає у вас токени месенджерів і не керує доставкою повідомлень у ваш канал. Ваша програма отримує повідомлення від користувача, надсилає текст і контекст у CallAIder, отримує відповідь асистента і сама доставляє її назад користувачу.

Ця інструкція написана для двох аудиторій:

  • клієнтів платформи, які хочуть зрозуміти, що саме потрібно налаштувати в кабінеті CallAIder;
  • розробників клієнтських інтеграцій, які будуть реалізовувати bridge server, payload, retry, handoff і доставку відповідей у зовнішній канал.

Коли використовувати External Conversation Bridge

Зовнішній API-канал варто використовувати, якщо у вас вже є власна точка контакту з клієнтом і ви хочете підключити до неї AI-асистента без перенесення каналу всередину CallAIder.

Типові сценарії:

  • ваш Telegram-бот вже працює у production і має власні команди, меню, правила груп і forum topics;
  • CRM вже приймає повідомлення з сайту, месенджерів або форми підтримки;
  • оператори працюють у власному інтерфейсі, а AI має відповідати тільки до моменту handoff;
  • потрібно підключити асистента до каналу, для якого в CallAIder немає готового native-конектора;
  • ви не хочете передавати CallAIder токени Telegram, WhatsApp, Instagram, Viber або іншої зовнішньої платформи.

У цьому режимі CallAIder працює як текстовий асистент для вашого каналу: приймає вхідний текст, підтримує контекст діалогу, за потреби обробляє вкладення, генерує текстову відповідь і повертає її вашій програмі.

Що залишається на вашій стороні

External Conversation Bridge не є готовим адаптером конкретного месенджера. Це важливо врахувати ще до початку розробки.

На стороні клієнтської програми залишаються:

  • отримання повідомлень із зовнішнього каналу;
  • зберігання токенів Telegram, WhatsApp, CRM або іншої системи;
  • webhook-и, polling, group topics, routing і логіка каналу;
  • перетворення вхідного повідомлення у payload CallAIder;
  • відправлення відповідей CallAIder назад користувачу;
  • операторський handoff;
  • retry, таймаути, черги і rate limiting;
  • фільтрація повідомлень, на які AI не має відповідати.

CallAIder відповідає за:

  • перевірку API-ключа;
  • пошук асистента за assistantId;
  • перевірку, що зовнішній API-канал увімкнений;
  • створення або відновлення діалогу асистента;
  • збереження історії повідомлень для контексту;
  • підтримку контексту за externalConversationId;
  • генерацію відповіді асистента;
  • блокування відповіді, якщо діалог переведено у paused або blocked;
  • автоматичний старт нового звернення після closed, коли користувач знову пише у той самий діалог;
  • базовий захист від повторної обробки останнього messageId.

Загальна схема роботи

У production-сценарії між вашим каналом і CallAIder зазвичай стоїть ваш backend або bridge server. Саме він зберігає секрети і робить server-to-server запити.

Користувач у зовнішньому каналі
        |
        v
Ваш backend / bridge server
        |
        | POST /v1/assistants/:assistantId/external-conversations/messages
        | Authorization: Bearer cld_...
        v
Публічний API CallAIder
        |
        v
Асистент CallAIder
        |
        v
JSON response: messages[]
        |
        v
Ваш backend доставляє messages[] у Telegram, CRM, сайт або інший канал

Не викликайте цей API напряму з браузера кінцевого користувача або з мобільного застосунку, якщо ключ можна буде витягнути з клієнтського коду. API-ключ має жити тільки на серверній стороні.

Що потрібно підготувати перед інтеграцією

Перед тим як писати код, переконайтесь, що у вас є:

  1. Активний асистент у CallAIder.
  2. Доступ до налаштувань цього асистента.
  3. Увімкнений зовнішній API-канал в асистенті.
  4. assistantId, який у кабінеті показується як Ідентифікатор бота в API.
  5. API-ключ компанії у форматі cld_....
  6. Backend-сервіс, який буде приймати повідомлення з вашого каналу і викликати API CallAIder.
  7. Base URL публічного API для вашого підключення, наприклад https://api.callaider.ai/v1.

Якщо у вашому тарифі або договорі доступ до API не активований, зверніться до менеджера або підтримки CallAIder. Ключ може існувати в кабінеті, але API-запити мають проходити перевірку доступності відповідної можливості для компанії.

Крок 1. Відкрийте вкладку каналів асистента

У кабінеті CallAIder відкрийте потрібного асистента для редагування. Далі перейдіть у вкладку Канали.

Шлях у кабінеті:

Асистенти -> Редагувати -> Канали

Вкладка Канали в налаштуваннях асистента

На цій сторінці можуть бути різні канали, наприклад Telegram і зовнішній API-канал. Для інтеграції через External Conversation Bridge потрібен саме блок Зовнішній API-канал.

Крок 2. Увімкніть зовнішній API-канал

У блоці Зовнішній API-канал увімкніть перемикач Увімкнути зовнішній API-канал і збережіть налаштування.

Налаштування зовнішнього API-каналу

Після збереження перевірте два моменти:

  • статус блоку має бути активним;
  • у полі Ідентифікатор бота в API має відображатися ID асистента.

Цей ID використовується в URL API як assistantId.

Приклад:

POST /v1/assistants/17/external-conversations/messages

У цьому прикладі 17 - це значення з поля Ідентифікатор бота в API.

Base URL публічного API не налаштовується у цьому блоці. Його потрібно взяти з документації або від команди CallAIder для вашого підключення. У прикладах нижче використовується умовний URL https://api.example.com/v1; у реальній інтеграції замініть його на актуальний.

Крок 3. Створіть API-ключ для доступу до API

API-ключ потрібен, щоб CallAIder міг перевірити, що запит надходить від вашої компанії. Це не Telegram token, не preview token віджета і не JWT користувача. Це окремий server-to-server ключ компанії.

У кабінеті відкрийте розділ Інтеграції і знайдіть блок API ключі. Натисніть Створити ключ.

Створення API-ключа в розділі Інтеграції

Рекомендований порядок:

  1. Відкрийте Інтеграції.
  2. У блоці API ключі натисніть Створити ключ.
  3. Вкажіть зрозумілу назву, наприклад external-bridge-production або crm-chat-staging.
  4. Скопіюйте ключ у форматі cld_....
  5. Збережіть ключ у секретах вашого backend-сервісу.
  6. Не додавайте ключ у frontend-код, мобільний застосунок, відкриті логи або публічний репозиторій.

Зазвичай після створення ключ у списку показується замасковано. Тому скопіюйте повне значення одразу під час створення і передайте його розробнику через безпечний канал.

Приклад змінних середовища на стороні вашого backend:

CALLAIDER_API_BASE_URL="https://api.callaider.ai/v1"
CALLAIDER_ASSISTANT_ID="17"
CALLAIDER_API_KEY="cld_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

У кожному HTTP-запиті ключ передається в заголовку Authorization:

Authorization: Bearer cld_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

OpenAPI-специфікація публічного API доступна в репозиторії NBM-Labs/callaider_openapi. Її зручно використовувати для перевірки контрактів, генерації клієнтів і синхронізації backend-розробки з актуальними endpoint-ами.

Endpoint для повідомлень

Основний endpoint приймає вхідне повідомлення користувача і синхронно повертає відповідь асистента.

POST /v1/assistants/:assistantId/external-conversations/messages
Authorization: Bearer <API_KEY>
Content-Type: application/json

Повний URL складається з base URL, ID асистента і шляху endpoint:

https://api.example.com/v1/assistants/17/external-conversations/messages

Endpoint працює синхронно:

  1. приймає повідомлення;
  2. знаходить або створює діалог за вашим externalConversationId;
  3. відновлює контекст цього діалогу;
  4. запускає асистента;
  5. чекає відповідь;
  6. повертає messages[].

Через це ваш HTTP client має мати достатній timeout. Для production-інтеграцій рекомендовано закладати не менше 30-60 секунд.

Мінімальний payload

Найменший коректний запит містить стабільний ID діалогу, ID користувача у вашому каналі, ID повідомлення і текст.

{
  "externalConversationId": "tg:123456",
  "externalUserId": "123456",
  "messageId": "789",
  "text": "Добрий день"
}

Приклад curl:

curl -X POST \
  "https://api.example.com/v1/assistants/17/external-conversations/messages" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_KEY>" \
  -d '{
    "externalConversationId": "tg:123456",
    "externalUserId": "123456",
    "messageId": "789",
    "text": "Добрий день"
  }'

Поля payload

ПолеТипОбов’язковеДля чого потрібно
externalConversationIdstringТакСтабільний ID діалогу на вашій стороні. За ним CallAIder відновлює контекст.
externalUserIdstringНіID користувача у вашому зовнішньому каналі. Корисний для аудиту і пошуку.
messageIdstringНі, але рекомендованоID вхідного повідомлення. Потрібен для захисту від повторної обробки при retry.
textstringТак, якщо немає attachmentsТекст, який буде передано асистенту.
attachmentsarrayНіДо 5 вкладень типу remote_url.
metadataobjectНіДодатковий JSON-контекст для аудиту або інтеграції.

Практично завжди варто передавати messageId, навіть якщо поле не є строго обов’язковим. Без нього вашій інтеграції буде складніше безпечно повторювати запит після timeout або мережевої помилки.

Як вибрати externalConversationId

externalConversationId - це ключ діалогу. Якщо він однаковий, CallAIder вважає повідомлення частиною одного контексту. Якщо він інший, буде створено інший діалог.

Вибирайте ID залежно від того, як у вашому каналі має працювати пам’ять асистента.

СценарійПриклад externalConversationIdПоведінка
Один діалог на Telegram-користувачаtg:user:123456Асистент пам’ятає контекст конкретного користувача.
Один діалог на Telegram-чатtg:chat:-1001234567890Контекст спільний для чату.
Окремий діалог на forum topictg:-1001234567890:topic:42Кожна тема має власний контекст.
Один діалог на CRM-тикетcrm:ticket:ABC-1001Контекст прив’язаний до звернення в CRM.
Один діалог на чат сайтуsite:conversation:9f3aКонтекст прив’язаний до звернення або чату на сайті.

Різні асистенти можуть використовувати однакові externalConversationId без конфлікту, тому що контекст прив’язується до комбінації:

assistantId + externalConversationId

Не змінюйте externalConversationId випадково між повідомленнями одного діалогу. Якщо кожне повідомлення матиме новий ID, асистент не зможе відновити контекст попередньої розмови.

Як використовувати messageId і retry

messageId має бути стабільним ID вхідного повідомлення у вашому каналі. Якщо ви повторюєте той самий запит через timeout, мережеву помилку або 5xx, використовуйте той самий messageId.

Правильна поведінка:

  • користувач надіслав повідомлення 789;
  • ваш bridge server відправив його в CallAIder з messageId: "789";
  • запит завис або відповідь не дійшла;
  • bridge server повторив запит з тим самим messageId: "789";
  • якщо CallAIder вже обробив це повідомлення, відповідь міститиме duplicate: true і порожній messages[].

Не генеруйте новий messageId для retry одного й того самого повідомлення. Інакше CallAIder сприйме retry як нове повідомлення користувача.

Захист від дублікатів розрахований на типовий retry останнього повідомлення у конкретному діалозі. Для високонавантажених інтеграцій все одно варто мати власну ідемпотентність або чергу на стороні клієнтського bridge server.

Повний payload з metadata і вкладенням

Коли потрібно передати додатковий контекст або файл, використовуйте metadata і attachments.

{
  "externalConversationId": "crm:ticket:ABC-1001",
  "externalUserId": "customer:555",
  "messageId": "crm-msg-9002",
  "text": "Перевір, будь ласка, цей рахунок",
  "attachments": [
    {
      "type": "remote_url",
      "url": "https://client.example.com/downloads/invoice-1001.pdf",
      "mimeType": "application/pdf",
      "filename": "invoice-1001.pdf",
      "sizeBytes": 245760,
      "sha256": "f2c7d0b4f4e8f1b7a12a4d34a9f6b7c0d1e2f3a4567890abcdef1234567890ab"
    }
  ],
  "metadata": {
    "source": "crm",
    "ticketId": "ABC-1001",
    "customerPlan": "business",
    "operatorQueue": "support-l2"
  }
}

metadata не керує логікою каналу автоматично, але допомагає з аудитом, трасуванням і майбутньою інтеграційною логікою. Передавайте туди тільки технічний і бізнес-контекст, який справді потрібен для вашого сценарію.

Attachments через remote_url

External Conversation Bridge підтримує inbound-вкладення у форматі remote_url. Це означає, що ваша програма не передає файл напряму в JSON. Замість цього вона передає короткоживучий HTTPS URL, за яким backend CallAIder може завантажити файл під час обробки повідомлення.

Формат одного attachment:

{
  "type": "remote_url",
  "url": "https://client.example.com/files/photo.jpg",
  "mimeType": "image/jpeg",
  "filename": "photo.jpg",
  "sizeBytes": 734003,
  "sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}

Поля attachment:

ПолеТипОбов’язковеОпис
typestringТакПоки підтримується тільки remote_url.
urlstringТакПублічний HTTPS URL, доступний backend-сервісу CallAIder під час обробки.
mimeTypestringНі, але рекомендованоОчікуваний MIME type. Особливо корисно для signed URL, які повертають application/octet-stream.
filenamestringНіНазва файла, яка буде відображатися в історії діалогу та під час обробки.
sizeBytesnumberНіОчікуваний розмір файла. Якщо він більший за ліміт, запит буде відхилено до скачування.
sha256stringНіSHA-256 checksum. Якщо передано, CallAIder перевірить файл після скачування.

Підтримувані типи файлів:

  • зображення: image/png, image/jpeg, image/webp, image/heic, image/heif, image/gif;
  • відео: video/mp4;
  • аудіо: audio/wav, audio/x-wav, audio/wave, audio/mp3, audio/mpeg, audio/aiff, audio/x-aiff, audio/aac, audio/ogg, audio/flac;
  • документи: application/pdf.

Поточні обмеження:

  • до 5 вкладень в одному повідомленні;
  • до 20 MB на один файл;
  • тільки https;
  • без username/password у URL;
  • hostname має резолвитись у публічну IP-адресу;
  • private, localhost, link-local, multicast і службові IP-діапазони блокуються;
  • redirect-и обмежені і перевіряються повторно;
  • Content-Length і фактичний розмір перевіряються;
  • після скачування файл готується до аналізу асистентом без повторного звернення до вашого URL.

Не передавайте довгоживучі URL з приватними секретами у query string. Краще використовуйте short-lived signed URL або власний одноразовий download endpoint, який живе достатньо довго для обробки одного повідомлення.

Успішна відповідь на повідомлення

Якщо повідомлення оброблено, API повертає ok: true, поточний статус діалогу і масив messages.

{
  "ok": true,
  "status": "active",
  "messages": [
    {
      "text": "Добрий день! Чим можу допомогти?"
    }
  ]
}

messages - це масив текстових відповідей, які ваша програма має доставити у свій канал у тому самому порядку. У більшості сценаріїв там буде один елемент, але контракт одразу зроблений масивом, щоб у майбутньому підтримувати кілька відповідей без зміни формату.

CallAIder не доставляє ці повідомлення в Telegram, CRM або месенджер самостійно. Доставка завжди на вашій стороні.

Відповідь на повторне повідомлення

Якщо messageId збігається з останнім обробленим inbound message у цій conversation, повторна генерація не запускається.

{
  "ok": true,
  "status": "active",
  "duplicate": true,
  "messages": []
}

Для вашої програми це означає: нічого не доставляти користувачу. Така відповідь є нормальною для retry, якщо попередній запит вже був оброблений.

Відповідь, коли AI поставлено на паузу

Якщо conversation має статус paused або blocked, асистент не відповідає на нові повідомлення.

{
  "ok": true,
  "status": "paused",
  "skipped": true,
  "messages": []
}

Це корисно для операторського handoff. Наприклад, оператор взяв діалог у роботу, ваша програма перевела conversation у paused, а всі наступні повідомлення користувача продовжують надходити в систему, але AI не втручається.

Закритий діалог і нове звернення

Статус closed означає, що поточне звернення завершене. Коли ви переводите діалог у closed, CallAIder завершує поточний контекст звернення.

Якщо користувач пізніше напише у той самий externalConversationId, вам не потрібно створювати новий діалог вручну. Просто надішліть звичайний POST /messages з новим messageId. CallAIder автоматично почне нове звернення і поверне діалог у active.

{
  "ok": true,
  "status": "active",
  "reopened": true,
  "messages": [
    {
      "text": "Добрий день! Чим можу допомогти?"
    }
  ]
}

Так можна використовувати один і той самий externalConversationId для довгострокового каналу, наприклад Telegram-чату, але розділяти окремі звернення через статус closed.

Endpoint для зміни стану діалогу

Другий endpoint керує станом діалогу. Він потрібен для handoff, повернення діалогу асистенту і закриття звернення.

POST /v1/assistants/:assistantId/external-conversations/:externalConversationId/state
Authorization: Bearer <API_KEY>
Content-Type: application/json

Приклад URL:

https://api.example.com/v1/assistants/17/external-conversations/tg:123456/state

Якщо externalConversationId містить символи, які мають спеціальне значення в URL, закодуйте його через URL encoding.

Підтримувані статуси

Вхідне значенняЗбережений статусПоведінка
activeactiveАсистент може відповідати.
ai_activeactiveAlias для active.
pausedpausedАсистент не відповідає.
handoffpausedAlias для paused, зручно для операторського сценарію.
human_handoffpausedAlias для paused.
closedclosedПоточне звернення закривається. Наступне inbound message відкриє нове звернення.
blockedblockedАсистент не відповідає.

Поставити діалог на паузу

Коли оператор бере діалог у роботу, переведіть conversation у paused або handoff.

curl -X POST \
  "https://api.example.com/v1/assistants/17/external-conversations/tg:123456/state" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_KEY>" \
  -d '{
    "status": "handoff",
    "metadata": {
      "reason": "operator_joined",
      "operatorId": "op-17"
    }
  }'

Очікувана відповідь:

{
  "ok": true,
  "status": "paused"
}

Після цього нові POST /messages для цього діалогу повертатимуть skipped: true і порожній messages[].

Повернути діалог асистенту

Якщо оператор завершив свою частину, але звернення ще не закрите, поверніть conversation у active.

curl -X POST \
  "https://api.example.com/v1/assistants/17/external-conversations/tg:123456/state" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_KEY>" \
  -d '{
    "status": "active",
    "metadata": {
      "reason": "operator_left"
    }
  }'

Наступне повідомлення користувача знову буде оброблено асистентом у межах поточного контексту.

Закрити звернення

Коли питання клієнта вирішене, переведіть conversation у closed.

curl -X POST \
  "https://api.example.com/v1/assistants/17/external-conversations/tg:123456/state" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_KEY>" \
  -d '{
    "status": "closed",
    "metadata": {
      "reason": "operator_closed_issue"
    }
  }'

Наступне повідомлення у той самий externalConversationId автоматично почне нове звернення. Це зручно для CRM-тикетів, підтримки в месенджерах і повторних звернень клієнта в той самий чат.

Приклад клієнтської інтеграції на TypeScript

Нижче спрощений приклад backend-коду. Він показує тільки взаємодію з CallAIder. Функція sendTextToExternalChannel має бути реалізована у вашій системі для конкретного каналу.

type BridgeMessage = {
  externalConversationId: string;
  externalUserId?: string;
  messageId?: string;
  text: string;
  metadata?: Record<string, unknown>;
};

type CallaiderBridgeResponse = {
  ok: boolean;
  status: string;
  duplicate?: boolean;
  skipped?: boolean;
  reopened?: boolean;
  messages: Array<{ text: string }>;
};

const baseUrl = process.env.CALLAIDER_API_BASE_URL;
const assistantId = process.env.CALLAIDER_ASSISTANT_ID;
const apiKey = process.env.CALLAIDER_API_KEY;

async function askCallaider(message: BridgeMessage): Promise<CallaiderBridgeResponse> {
  const response = await fetch(
    `${baseUrl}/assistants/${assistantId}/external-conversations/messages`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${apiKey}`,
      },
      body: JSON.stringify(message),
    },
  );

  if (!response.ok) {
    const text = await response.text();
    throw new Error(`CallAIder bridge failed: ${response.status} ${text}`);
  }

  return response.json() as Promise<CallaiderBridgeResponse>;
}

async function handleIncomingText(input: {
  chatId: string;
  userId: string;
  messageId: string;
  text: string;
}) {
  const result = await askCallaider({
    externalConversationId: `tg:chat:${input.chatId}`,
    externalUserId: input.userId,
    messageId: input.messageId,
    text: input.text,
    metadata: {
      source: 'telegram',
      chatId: input.chatId,
    },
  });

  if (result.duplicate || result.skipped) {
    return;
  }

  for (const message of result.messages) {
    const text = message.text.trim();
    if (text) {
      await sendTextToExternalChannel(input.chatId, text);
    }
  }
}

Для production-коду додайте:

  • HTTP timeout;
  • retry тільки з тим самим messageId;
  • логування request id або власного correlation id;
  • чергу або lock на один externalConversationId, якщо ваш канал очікує послідовний діалог;
  • обробку помилок доставки відповіді у зовнішній канал.

Приклад для Telegram forum topic

Якщо ви використовуєте Telegram-групу з forum topics, сформуйте externalConversationId так, щоб різні теми не змішували контекст.

curl -X POST \
  "https://api.example.com/v1/assistants/17/external-conversations/messages" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_KEY>" \
  -d '{
    "externalConversationId": "tg:-1001234567890:topic:42",
    "externalUserId": "123456",
    "messageId": "789",
    "text": "Потрібна допомога з оплатою",
    "metadata": {
      "chatId": "-1001234567890",
      "messageThreadId": 42,
      "username": "client_user"
    }
  }'

У такій схемі topic 42 матиме окремий контекст від topic 43, навіть якщо це одна Telegram-група.

Приклад для CRM-чату

Для CRM часто найзручніше прив’язувати conversation до тикета або звернення.

curl -X POST \
  "https://api.example.com/v1/assistants/17/external-conversations/messages" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <API_KEY>" \
  -d '{
    "externalConversationId": "crm:ticket:ABC-1001",
    "externalUserId": "customer:555",
    "messageId": "crm-msg-9001",
    "text": "Я хочу змінити дату запису",
    "metadata": {
      "crm": "custom",
      "ticketId": "ABC-1001",
      "priority": "normal"
    }
  }'

Коли оператор закрив тикет, викличте /state зі статусом closed. Якщо клієнт створить новий тикет, використовуйте новий externalConversationId. Якщо клієнт напише у той самий довгий чат, але ви хочете почати нове звернення, також можна закрити старий діалог і дозволити CallAIder почати новий контекст при наступному повідомленні.

Типовий handoff-сценарій

Операторський handoff зазвичай виглядає так:

  1. Користувач пише у Telegram, WhatsApp, CRM або чат сайту.
  2. Ваш backend надсилає повідомлення у POST /messages.
  3. CallAIder повертає messages[].
  4. Ваш backend доставляє відповідь користувачу.
  5. Оператор вирішує взяти діалог.
  6. Ваш backend викликає /state зі статусом handoff або paused.
  7. Нові повідомлення користувача можуть продовжувати надходити у CallAIder, але API повертатиме skipped: true.
  8. Якщо оператор повертає діалог AI, backend викликає /state зі статусом active.
  9. Якщо оператор закрив питання, backend викликає /state зі статусом closed.
  10. Коли користувач напише знову, звичайний POST /messages автоматично почне нове звернення.

Цей підхід дозволяє не змішувати відповіді AI і людини. Головне - щоб саме ваша система була джерелом правди щодо того, хто зараз веде діалог.

Таймаути, черги і паралельні повідомлення

/messages повертає відповідь тільки після генерації асистента, тому інтеграція має бути готова до довших HTTP-запитів.

Рекомендації:

  • ставте timeout 30-60 секунд або більше, якщо ваш сценарій допускає довші відповіді;
  • повторюйте запит тільки з тим самим messageId;
  • не запускайте багато паралельних запитів для одного externalConversationId, якщо порядок відповідей важливий;
  • використовуйте queue або lock на рівні conversation;
  • логування робіть так, щоб можна було знайти externalConversationId, messageId, HTTP status і час обробки;
  • якщо відповідь CallAIder отримана, але доставка у зовнішній канал не вдалася, повторюйте саме доставку у канал, а не генерацію нової відповіді без потреби.

External Conversation Bridge повертає відповідь синхронно у HTTP response. Окремий callback URL у цьому сценарії не потрібен.

Помилки і діагностика

Типові HTTP-статуси:

HTTP statusЩо перевірити
400Payload неповний або некоректний: немає externalConversationId, немає text при відсутніх attachments, передано некоректний status.
403Асистент або зовнішній API-канал вимкнений, або компанія не має доступу до потрібної API-можливості.
404Для цього асистента не знайдено увімкнену конфігурацію external bridge в межах компанії API-ключа.
500Неочікувана серверна помилка. Запит можна повторити з тим самим messageId.

Якщо запит не проходить авторизацію, перевірте:

  • чи передано заголовок Authorization: Bearer <API_KEY>;
  • чи не обрізаний API-ключ;
  • чи ключ активний у кабінеті;
  • чи ключ належить тій самій компанії, що й асистент;
  • чи доступ до API активований для вашої компанії.

Якщо помилка сталася під час обробки повідомлення асистентом, API може повернути стандартний текст помилки як звичайний елемент у messages[]. У такому випадку ваша програма доставляє його користувачу так само, як інші відповіді асистента, або обробляє за власними правилами підтримки.

Безпека

Для безпечної інтеграції дотримуйтесь таких правил:

  • використовуйте тільки HTTPS;
  • зберігайте API-ключ тільки на backend-стороні;
  • не викликайте External Conversation Bridge напряму з браузера користувача;
  • не додавайте API-ключ у mobile app, frontend bundle або публічний репозиторій;
  • не логувати API-ключ у відкриті логи;
  • не передавайте токени Telegram, WhatsApp, CRM або інших каналів у CallAIder;
  • обмежте доступ до вашого bridge server;
  • додайте rate limiting на reverse proxy або на рівні backend;
  • для attachments використовуйте короткоживучі URL;
  • для production-інтеграцій ведіть аудит запитів без секретів.

API-ключ і токен зовнішнього каналу - це різні секрети. API-ключ дозволяє вашому backend звертатися до CallAIder. Токен Telegram, WhatsApp або CRM дозволяє вашому backend працювати з відповідним каналом. Не змішуйте їх і не передавайте токени каналів у payload CallAIder.

Чеклист перевірки інтеграції

Перед запуском у production пройдіть повний тестовий сценарій:

  1. Увімкніть Зовнішній API-канал у вкладці Канали.
  2. Скопіюйте Ідентифікатор бота в API.
  3. Створіть API-ключ у розділі Інтеграції.
  4. Збережіть CALLAIDER_API_BASE_URL, CALLAIDER_ASSISTANT_ID і CALLAIDER_API_KEY у backend-секретах.
  5. Надішліть тестовий POST /messages з унікальним externalConversationId.
  6. Переконайтесь, що відповідь містить ok: true, status: active і непорожній messages[].
  7. Повторіть той самий запит з тим самим messageId.
  8. Переконайтесь, що відповідь містить duplicate: true і messages: [].
  9. Викличте /state зі статусом paused або handoff.
  10. Надішліть нове повідомлення у той самий externalConversationId.
  11. Переконайтесь, що відповідь містить skipped: true і messages: [].
  12. Викличте /state зі статусом active.
  13. Надішліть нове повідомлення і перевірте, що асистент знову відповідає.
  14. Викличте /state зі статусом closed.
  15. Надішліть нове повідомлення з тим самим externalConversationId і новим messageId.
  16. Переконайтесь, що відповідь містить status: active, reopened: true і нові messages[].
  17. Перевірте доставку відповіді у ваш реальний канал.
  18. Перевірте retry, timeout і логування на стороні вашого bridge server.

Короткий підсумок

External Conversation Bridge підходить для інтеграцій, де клієнтська система сама володіє каналом, токенами, операторською логікою і доставкою повідомлень. CallAIder у цій схемі відповідає за AI-частину: контекст діалогу, обробку повідомлення і генерацію відповіді.

Для запуску потрібні три базові речі: увімкнений зовнішній API-канал в асистенті, assistantId з кабінету і API-ключ компанії. Далі ваш backend надсилає повідомлення в /messages, доставляє messages[] у свій канал і за потреби керує станом діалогу через /state.