Публичен API и уебхукове
REST API позволява на вашата CRM, бекенда на сайта или собствен бот да работят с лийдове и разговори в cenaly.com по същия начин, както го прави панелът: да четат карти, да променят свойства, да изпращат събития, да отговарят в разговори. Уебхуковете, обратно, сами уведомяват вашия сървър за нови лийдове, събития и съобщения.
Какво е нужно за стартиране#
| Какво | Къде |
|---|---|
| Роля | собственик на акаунта — ключът действа от негово име; служителите не виждат ключове |
| Раздел | включен чат (лийдовете и разговорите живеят в него) |
| Ключ | Интеграции → API и уебхукове → Ключове → Създаване на ключ |
| Адрес на API | https://api.cenaly.com/public-api/v1 |
| Справочник по методи | api.cenaly.com/public-api/v1/docs |
| OpenAPI 3.1 | api.cenaly.com/public-api/v1/openapi.json |
Стъпка 1. Ключ и права#
- Отворете Интеграции и натиснете API и уебхукове в горната част на страницата.
- В раздела Ключове натиснете Създаване на ключ, въведете име („CRM“, „Бекенд на сайта“, „Telegram бот“).
- Изберете набор от права или отбележете правата поотделно, при няколко обекта — до кои обекти има достъп ключът, и срок.
- Копирайте секрета
cnk_…. Той се показва само веднъж: при нас се съхранява само неговият отпечатък (SHA-256).
| Право | Какво дава |
|---|---|
contacts:read |
списък, търсене и карта на лийд, свойства |
contacts:write |
промяна на свойства и тагове, обединяване, изтриване по искане на субекта |
events:read |
поток от събития на лийда |
events:write |
изпращане на събития (покупка, регистрация, всяко свое) |
conversations:read |
разговори и съобщения |
conversations:write |
отговор на оператор, бележка, статус, отговорник, етикети |
messages:send |
съобщение до лийд в последния му разговор |
segments:read |
запазени сегменти и техните участници |
Набори: CRM / бекенд (лийдове, свойства, събития, четене на разговори и сегменти), Свой бот (чете лийдове, отговаря в разговори, пише на лийдове), Само четене, Всичко.
- Права променят правата и обектите на ключа в движение — интеграцията продължава да работи.
- Преиздаване издава нов секрет; старият спира да работи веднага, правата и журналът остават.
- Отнемане — веднага и завинаги, следващата заявка ще получи
401.
Ключът се предава със заглавка Authorization: Bearer cnk_… (или X-Api-Key: cnk_…). Съхранявайте го само на сървъра — не в браузъра и не в мобилното приложение.
Стъпка 2. Първи заявки#
KEY=cnk_ваш_ключ
API=https://api.cenaly.com/public-api/v1
# какво може ключът: права, обекти, лимит
curl -H "Authorization: Bearer $KEY" "$API/me"
# лийдове, най-нови отгоре (20 на страница)
curl -H "Authorization: Bearer $KEY" "$API/contacts?limit=20"
# търсене по име, имейл или телефон
curl -H "Authorization: Bearer $KEY" "$API/contacts?q=anna"
Отговорът е винаги плик:
{
"meta": { "status": 200, "requestId": "…", "nextCursor": "WyIyMDI2LTEwLTA0…" },
"data": { "total": 128, "contacts": [ { "id": "…", "status": "lead", "name": "Анна", "updatedAt": "2026-10-04T09:12:00Z" } ] }
}
Следващата страница е същата заявка с cursor=<meta.nextCursor>; няма nextCursor — няма повече страници. Грешка — {"meta": {...}, "error": {"code": "…"}}.
Обект. Ако на ключа е достъпен един обект, не е нужно да го указвате. Ако са няколко — добавете location=<id на обект> (списък — GET /v1/locations), иначе отговор 400 location_required.
Свойства и събития по вашия user_id. Не е задължително да съхранявате нашия id на лийда: добавете ?by=user_id и адресът ще стане вашият идентификатор на потребителя. Ако лийд с такъв user_id още няма — ще го създадем.
# свойства: операции update_or_create, set_once, add, delete, append, union, exclude
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"ops":[{"op":"update_or_create","key":"$email","value":"anna@example.com"},
{"op":"add","key":"orders","value":1},
{"op":"union","key":"interests","value":"yoga"}]}' \
"$API/contacts/u-1001/props?by=user_id"
# събития на пакети (до 100), повторение със същия id се брои веднъж
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"events":[{"id":"ord-5531","userId":"u-1001","name":"order_paid","props":{"sum":45,"currency":"GEL"}}]}' \
"$API/events"
# отговор в разговор и промяна на статус
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"text":"Здравейте! Поръчката е предадена на куриер."}' "$API/conversations/<id>/reply"
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"status":"resolved"}' "$API/conversations/<id>/status"
Записът минава през същата опашка, както промените от панела: промените се прилагат за секунди, а отговорът на запис по user_id — 202 с pending. В картата на лийда авторът на промяната се вижда като „API: <име на ключа>“.
Методи#
| Метод | Какво прави | Право |
|---|---|---|
GET /v1/me, GET /v1/locations |
ключ, права, обекти | — |
GET /v1/contacts |
лийдове: status (anon, lead, user), q, limit, cursor |
contacts:read |
GET /v1/contacts/{id} |
картон: свойства, тагове, обобщение на събития | contacts:read |
POST /v1/contacts/{id}/props |
свойства с операции (до 250) или {"props":{…}} |
contacts:write |
POST / DELETE /v1/contacts/{id}/tags |
тагове на лийда | contacts:write |
POST /v1/contacts/merge |
слепване на два картона: преглед, след това "apply": true |
contacts:write |
POST /v1/contacts/{id}/erase |
изтриване по искане на субекта ("confirm": true) |
contacts:write |
GET /v1/contacts/{id}/events |
поток от събития на лийда | events:read |
POST /v1/contacts/{id}/events, POST /v1/events |
събития (до 100 наведнъж) | events:write |
GET /v1/contacts/{id}/conversations |
разговори на лийда | conversations:read |
POST /v1/contacts/{id}/messages |
писане на лийда в последния му разговор | messages:send |
GET /v1/conversations, /{id}, /{id}/messages |
разговори и съобщения | conversations:read |
POST /v1/conversations/{id}/reply |
отговор на оператор или бележка ("type": "note") |
conversations:write |
POST /v1/conversations/{id}/status, /assign, /tags |
статус, отговорник, етикети | conversations:write |
GET /v1/segments, GET /v1/segments/{id}/contacts |
сегменти и техните участници | segments:read |
Полета, примери и кодове за грешки на всеки метод — в справочника.
Стъпка 3. Уебхукове#
Уебхуковете се настройват за обект: Чат → Настройки → Уебхукове — адрес https, събития и тайна за подпис. Към събитията на разговора (нов разговор, ново съобщение, статус, оценка, етикети) се добавиха събития на лийда:
| Събитие | Кога пристига |
|---|---|
lead.created |
появил се е нов лийд (контакт, оставен в чата, във форма или създаден през API) |
lead.props_changed |
свойствата на лийда са се променили — списък с ключове в changedProps |
lead.event_tracked |
лийдът има събитие; по подразбиране всички ваши събития без системните ($…), списъкът с имена се задава с полето „Кои събития да се изпращат“ |
message.goal_reached |
лийдът е достигнал целта на съобщението (поп-ъп, разпращане) |
{
"event": "lead.event_tracked",
"deliveryId": "WD…",
"at": "2026-10-04T09:12:03Z",
"webhookId": "…",
"locationId": "…",
"contact": { "id": "…", "status": "user", "userId": "u-1001", "props": { "orders": 3 }, "tags": ["vip"] },
"leadEvent": { "name": "order_paid", "at": "2026-10-04T09:12:00Z", "props": { "sum": 45 } }
}
Името, имейлът и телефонът на лийда попадат в тялото, само ако при уебхука е включена отметката „Предаване на контактите на госта“. Проверка на подписа — HMAC-SHA256 на тялото с тайната на уебхука:
# заголовък X-Meni-Signature: sha256=<hex>
echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET"
Отговорете с 2xx за 5 секунди — иначе доставката ще се повтори; след дълга серия от неуспехи уебхукът се изключва и това се вижда в настройките. Дубликатите отсейте по deliveryId.
Ограничения#
- 60 заявки в минута на ключ; над това —
429със заголовъкRetry-After. Остатъкът — вX-RateLimit-*. - До 200 записа на страница (по подразбиране 50), до 100 събития в пакет, до 250 операции със свойства за заявка, до 30 тага наведнъж.
- До 20 активни ключа на акаунт.
- Журналът с извиквания на ключа се пази 30 дни: време, метод, шаблон на пътя (без id и тяло), код на отговора, продължителност.
- Събитията, изпратени през API, попадат в картона на лийда (поток, обобщение, сегменти), но не в суровия журнал на аналитиката на посещенията.
- На лийд може да се пише само в съществуващ разговор: няма разговор — отговор
409 no_conversation.
Отстраняване на проблеми#
| Отговор | Какво означава | Какво да направите |
|---|---|---|
401 api_key_missing / api_key_invalid |
ключът липсва в заявката, или е невалиден, отменен или изтекъл | проверете заголовъка; при нужда издайте ключа наново |
403 scope_required |
ключът няма нужното право | Права на ключа — добавете право |
400 location_required |
на ключа са достъпни няколко обекта | добавете location=<id> |
404 location_not_found |
обектът не е сред обектите на ключа | сверте се с GET /v1/locations |
404 not_found |
лийдът или разговорът липсва | за своите id използвайте ?by=user_id |
429 rate_limited |
повече от 60 заявки в минута | изчакайте Retry-After секунди, групирайте събитията в пакети |
502 upstream_error |
временен сбой | повторете заявката по-късно |
Всяка заявка се вижда в раздела Журнал с извиквания — с код на отговора и име на ключа.
Често задавани въпроси#
Може ли API да се извиква от браузър? Не: ключът дава достъп до всички лийдове на обекта. Извиквайте API от своя сървър.
С какво user_id се различава от id на лийда? id на лийда е нашият идентификатор на картона. user_id е вашият идентификатор на потребителя (номер в CRM, id на акаунт в сайта); с ?by=user_id картонът може да бъде адресиран чрез него.
Какво се случва при повторно изпращане на събитие? Събитие със същия id се отчита веднъж — може спокойно да повторите заявката при мрежова грешка.
Нужен ли е отделен ключ за всеки обект? Не: един ключ може да работи с няколко обекта, обектът се избира с параметъра location.
Свързани раздели#
- Чат с гости — разговори, лийдове и настройки на уебхукове
- Заявки — единен входящ поток
- API и MCP на собствената АТС — отделен API за телефония
- Интеграции