Кастомні канали · Webhooks
Кастомні канали
Підключіть до Pager будь-яку платформу повідомлень: повідомлення клієнтів потраплять у спільний інбокс, а відповіді менеджерів — назад на вашу платформу.
Кастомний канал підключає до Pager платформу, для якої немає готової інтеграції: власний месенджер, чат на сайті, мобільний застосунок чи сторонній сервіс. Менеджери працюють із такими розмовами так само, як з Instagram чи Telegram.
Клієнт написав на вашій платформі — ви надсилаєте message.created на вебхук Pager.
Менеджер відповів у Pager — Pager надсилає POST на URL вашого сервера.
- Обмін іде JSON-ом через HTTPS, обидва напрямки підписано одним ключем каналу.
- Клієнтів і повідомлення ви ідентифікуєте своїми ідентифікаторами —
externalId. Клієнта й розмову Pager створює сам під час першого повідомлення. - Нові повідомлення враховуються в ліміті повідомлень організації, як і в інших каналах.
Канал підключає адміністратор організації в кабінеті Pager.
- 1
Відкрийте Налаштування → Канали і натисніть Підключити власний канал.
- 2
Вкажіть назву каналу — її бачитимуть менеджери в інбоксі — і URL для відправки повідомлень: адресу вашого сервера, на яку Pager надсилатиме відповіді менеджерів.
- 3
Після підключення в картці каналу з'явиться API Key виду
chan_sk_live_…— це ключ каналу. Додайте його в налаштування свого сервера.
pgr_live_…). У кожного кастомного каналу свій ключ.Обидва напрямки підписуються ключем каналу в заголовку x-channel-key.
- Ваш сервер → Pager. Передавайте ключ у
x-channel-keyкожного запиту до вебхука. За ключем Pager визначає канал і організацію. - Pager → ваш сервер. Pager передає той самий ключ у
x-channel-key. Порівнюйте його зі своїм і відхиляйте запити з іншим ключем — інакше будь-хто, хто знає ваш URL, зможе надіслати вашим клієнтам повідомлення від імені менеджера.
Усі вхідні події надсилайте запитом POST на одну адресу. Тип події задає поле event: message.created, message.edited або message.deleted.
https://api.pager.co.ua/api/webhooks/customЗаголовки
x-channel-keyheaderКлюч каналу.
Content-Typeheaderapplication/json.
- Одна подія — один запит. Пакетної відправки немає.
- Успішна обробка — завжди
200з полемaction, що показує, що саме зроблено — див. Відповіді та помилки.
Нове повідомленняmessage.created
Додає повідомлення в розмову з клієнтом. Якщо клієнт пише вперше, Pager створює клієнта й розмову.
Тіло запиту
eventstringобов'язковийmessage.created.client.externalIdstringобов'язковийІдентифікатор клієнта на вашій платформі, до 255 символів. За ним Pager знаходить клієнта в цьому каналі.
client.namestring | nullнеобов'язковийІм'я клієнта, до 100 символів. Не передано — лишається як було;
null— стерти.client.imageUrlstring | nullнеобов'язковийПосилання на аватар. Не передано — лишається як було.
message.externalIdstringобов'язковийІдентифікатор повідомлення на вашій платформі, до 255 символів.
message.directionenumобов'язковийincoming— повідомлення від клієнта.outgoing— повідомлення, яке ваш оператор надіслав поза Pager: так історія в Pager лишається повною.message.textstring | nullнеобов'язковийТекст, до 8000 символів.
message.attachmentsobject[]необов'язковийДо 20 вкладень — див. Вкладення.
- Повторна подія з тим самим
message.externalIdне створює дубль: Pager оновлює текст і вкладення наявного повідомлення. Тож запит можна безпечно повторити, якщо ви не отримали відповіді. - Вхідне повідомлення позначає розмову непрочитаною. Вихідне зберігається прочитаним і стану розмови не змінює.
- Пробіли на початку й у кінці тексту обрізаються, переноси
\r\nзамінюються на\n. - Нове повідомлення враховується в ліміті повідомлень організації; повтор того самого
externalId— ні.
curl -X POST "https://api.pager.co.ua/api/webhooks/custom" \
-H "x-channel-key: $PAGER_CHANNEL_KEY" \
-H "Content-Type: application/json" \
-d '{"event":"message.created","client":{"externalId":"user_58213","name":"Олена Коваль","imageUrl":"https://cdn.example.com/avatars/58213.jpg"},"message":{"externalId":"msg_90412","direction":"incoming","text":"Добрий день! Чи є доставка у Львів?","attachments":[]}}'{
"ok": true,
"action": "created_or_deduped"
}Редагуванняmessage.edited
Змінює текст або вкладення повідомлення, яке Pager уже отримав, і позначає його відредагованим.
Тіло запиту
eventstringобов'язковийmessage.edited.message.externalIdstringобов'язковийІдентифікатор повідомлення, яке треба змінити.
message.textstring | nullнеобов'язковийНовий текст, до 8000 символів. Не передано,
nullабо порожній рядок — текст не змінюється.message.attachmentsobject[]необов'язковийНовий список вкладень, який повністю замінює попередній. Не передано — вкладення не змінюються;
[]— прибрати всі.
- Pager шукає повідомлення з цим
externalIdу вашому каналі — і вхідне, і вихідне. Повідомлення менеджерів маютьexternalId, лише якщо ваш сервер повернув його у відповіді на вихідне повідомлення. - Якщо повідомлення не знайдено, Pager відповідає
200зaction: "edit_ignored_not_found".
curl -X POST "https://api.pager.co.ua/api/webhooks/custom" \
-H "x-channel-key: $PAGER_CHANNEL_KEY" \
-H "Content-Type: application/json" \
-d '{"event":"message.edited","message":{"externalId":"msg_90412","text":"Добрий день! Чи є доставка у Львів і скільки вона коштує?"}}'{
"ok": true,
"action": "edited"
}Видаленняmessage.deleted
Видаляє повідомлення з Pager назавжди — менеджери більше його не бачать.
Тіло запиту
eventstringобов'язковийmessage.deleted.message.externalIdstringобов'язковийІдентифікатор повідомлення, яке треба видалити.
- Як і редагування, працює для вхідних і вихідних повідомлень із відомим
externalId. - Якщо повідомлення не знайдено, Pager відповідає
200зaction: "delete_ignored_not_found".
curl -X POST "https://api.pager.co.ua/api/webhooks/custom" \
-H "x-channel-key: $PAGER_CHANNEL_KEY" \
-H "Content-Type: application/json" \
-d '{"event":"message.deleted","message":{"externalId":"msg_90412"}}'{
"ok": true,
"action": "deleted"
}Успішна обробка — завжди 200 з ok: true. Поле action показує, що зроблено:
| action | Що означає |
|---|---|
created_or_deduped | Повідомлення створено або вже існувало й було оновлене (повтор message.created). |
edited | Повідомлення змінено. |
edit_ignored_not_found | Повідомлення з таким externalId не знайдено — нічого не змінено. |
deleted | Повідомлення видалено. |
delete_ignored_not_found | Повідомлення з таким externalId не знайдено — нічого не видалено. |
Помилки повертаються з полем error:
| HTTP | error | Що означає |
|---|---|---|
400 | Invalid payload | Тіло не відповідає схемі. Поле details.fieldErrors показує, які поля не пройшли перевірку. |
401 | Missing x-channel-key | Заголовок x-channel-key не передано. |
401 | Invalid channel key | Ключа не існує — можливо, канал видалено. |
403 | Channel is not Custom | Ключ належить каналу іншого типу. |
403 | Inbound disabled | Прийом вхідних повідомлень для каналу вимкнено. |
500 | Server error | Внутрішня помилка Pager. |
{
"error": "Invalid payload",
"details": {
"formErrors": [],
"fieldErrors": {
"client": [
"Invalid input: expected string, received undefined"
]
}
}
}500 і мережеві помилки повторіть запит із паузою — завдяки externalId повтор не створить дублів. Помилки 4xx повтор не виправить: перевірте ключ і тіло запиту.Коли менеджер надсилає повідомлення в розмові цього каналу, Pager одразу робить POST на URL, вказаний під час підключення. Так само приходять повідомлення, надіслані через Pager API. Формат тіла той самий, що й у вхідної події message.created.
Заголовки
x-channel-keystringКлюч каналу — перевіряйте його, див. Автентифікація.
Content-Typestringapplication/json.
Тіло запиту
eventstringЗавжди
message.created.client.externalIdstringІдентифікатор клієнта на вашій платформі — той, що ви передали у вхідних подіях.
message.pagerMessageIdstringІдентифікатор повідомлення в Pager. За ним відкидайте повторні доставки.
message.directionenumЗавжди
outgoing.message.textstring | nullТекст або
null, якщо менеджер надіслав лише файли.message.attachmentsobject[]Вкладення — див. Вкладення.
{
"event": "message.created",
"client": {
"externalId": "user_58213"
},
"message": {
"pagerMessageId": "c9d8e7f6-a5b4-4c3d-9e2f-1a0b9c8d7e6f",
"direction": "outgoing",
"text": "Так, відправляємо Новою поштою, доставка 1–2 дні. Ось тарифи:",
"attachments": [
{
"type": "image",
"payload": {
"url": "https://files.pager.co.ua/…/tariffs.png?X-Amz-Expires=86400&…"
}
}
]
}
}Відповідайте кодом 2xx, щойно прийняли повідомлення, і поверніть у JSON ідентифікатор повідомлення на вашій платформі.
{
"externalMessageId": "msg_90415"
}externalMessageId(абоexternalId) Pager зберігає як ідентифікатор повідомлення. Без нього ви не зможете потім відредагувати чи видалити це повідомлення.- Повідомлення враховується в ліміті повідомлень організації, лише якщо ви відповіли
2xxі повернулиexternalMessageId. - Менеджер бачить повідомлення надісланим лише після вашої відповіді, тож відповідайте швидко, а важку обробку робіть уже після неї.
Доставка й повтори
- Якщо ваш сервер відповів не
2xxабо не відповів зовсім, Pager повторює запит ще двічі — через 0,2 і 0,6 секунди. - Якщо всі три спроби невдалі, повідомлення зберігається в Pager із позначкою «не доставлено», і менеджер це бачить. Пізніше Pager його автоматично не надсилає.
message.pagerMessageId.Мінімальний обробник: перевіряє ключ, відкидає повтори, передає повідомлення на вашу платформу й повертає його ідентифікатор.
import express from "express"
const app = express()
app.use(express.json())
// pagerMessageId -> externalMessageId. У продакшні — база або Redis
const processed = new Map()
app.post("/pager/outbound", async (req, res) => {
// 1. Запит справді від Pager
if (req.get("x-channel-key") !== process.env.PAGER_CHANNEL_KEY) {
return res.status(401).end()
}
const { client, message } = req.body
// 2. Повтор уже обробленого повідомлення
if (processed.has(message.pagerMessageId)) {
return res.json({ externalMessageId: processed.get(message.pagerMessageId) })
}
// 3. Відправка клієнту на вашій платформі
const sent = await platform.sendMessage({
to: client.externalId,
text: message.text,
attachments: message.attachments,
})
processed.set(message.pagerMessageId, sent.id)
res.json({ externalMessageId: sent.id })
})platform.sendMessage — ваш код відправки. Решту можна брати як є.Вкладення передаються масивом об'єктів { type, payload: { url } } — однаково у вхідних подіях і в запитах від Pager.
Поля
typeenumобов'язковийimage,video,audio,documentабоfile.payload.urlstringобов'язковийПублічне посилання
http(s)на файл, до 2000 символів.
[
{
"type": "image",
"payload": {
"url": "https://cdn.example.com/uploads/photo.jpg"
}
},
{
"type": "document",
"payload": {
"url": "https://cdn.example.com/uploads/invoice.pdf"
}
}
]- Pager зберігає посилання, а не копію файлу, тож воно має лишатися доступним, поки менеджерам потрібна історія розмови.
- Посилання на
localhostі внутрішні мережі (127.*,10.*,192.168.*) Pager мовчки відкидає, решта повідомлення зберігається. - До 20 вкладень в одному повідомленні.
- У запитах від Pager посилання тимчасові й діють 24 години — завантажуйте файли одразу.