Документация
Веб-хуки
Веб-хуки отправляют внешним системам уведомления о создании, изменении и удалении данных организации.
Веб-хуки
Создать и настроить точки доставки можно в настройках веб-хуков. Секреты авторизации и секретные заголовки хранятся зашифрованными и не показываются после сохранения.
Какие события отправляются
В настройках выбираются действия: создание, изменение и удаление. Список сущностей отдельно не задается: веб-хук получает все поддерживаемые изменения организации, которые подходят под выбранные действия. Финансовые события отправляются на тех же условиях: FinanceOperation — операции, FinanceObligation — обязательства, FinanceRecurring — регулярные шаблоны, FinanceCategory — категории, FinanceMethod — способы оплаты. Например, finance_operation.create сообщает о новой операции, finance_obligation.update — об изменении расчёта.
| Действие | Событие | Данные |
|---|---|---|
| Создание | *.create | В payload передается новое состояние объекта в after. |
| Изменение | *.update | В payload передаются before, after и changed; customFields детализируется по uid полей, остальные JSON-поля передаются целиком. |
| Удаление | *.delete | В payload передается последнее известное состояние объекта в before. При отмене финансовой операции событие finance_operation.delete также содержит after со state=cancelled: сама операция сохраняется в истории. Замена операции создаёт update прежнего ID и create новых операций, а не delete. |
Авторизация и заголовки
Для точки доставки можно выбрать без авторизации, Bearer token или Basic auth. Также можно добавить обычные и секретные заголовки. Секретные значения не возвращаются в интерфейс после сохранения.
HTTP-запрос
Веб-хуки доступны на действующем тарифе «Бизнес» и отправляются POST-запросом на указанный URL. Используйте публичный HTTPS-адрес: .local, localhost и внутренние адреса не подходят. Тело запроса — JSON. Ответ с HTTP-статусом 2xx считается успешной доставкой. Финансовое событие становится доступным для отправки после успешного сохранения всей операции; ошибка сохранения очереди откатывает изменения.
| Заголовок | Значение |
|---|---|
Content-Type | Всегда application/json; charset=utf-8. |
X-Nakogda-Event | Название события, например clients.update. |
X-Nakogda-Delivery | Уникальный идентификатор доставки. Используйте его для дедупликации. |
X-Nakogda-Signature | Добавляется, если задан секрет подписи. |
Authorization | Добавляется при Bearer token или Basic auth. |
Формат payload
Поле event состоит из имени сущности и действия. before и after добавляются только когда соответствующее состояние есть в событии. changed заполняется для изменений: обычные поля сравниваются по верхнему уровню, а customFields раскрывается по uid измененных полей. В финансовых событиях cause содержит исходное действие, например save_operation или correct_operation; суммы передаются точными строками с двумя десятичными знаками, ID — числами, schema_version=1 задаёт версию снимка, timezone — часовой пояс дат. Распределения, начисления и история сроков передаются массивами; изменившийся массив приходит целиком.
{
"id": 12345,
"event": "clients.update",
"type": "update",
"entity": "Clients",
"entity_id": 77,
"organization_id": 12,
"user_id": 5,
"api_key_id": null,
"created_at": "2026-07-08 12:30:00",
"before": {
"id": 77,
"name": "Анна"
},
"after": {
"id": 77,
"name": "Анна Иванова"
},
"changed": {
"name": {
"before": "Анна",
"after": "Анна Иванова"
}
}
}Финансовые события
Финансовые изменения приходят на те же адреса веб-хуков организации и учитывают выбранные действия: создание, изменение и удаление. Отдельно подписываться на финансовые сущности не нужно. Для доставки действуют общие настройки и действующий тариф «Бизнес».
Финансовый API принимает сессию кабинета и стандартные API-ключи с правами их пользователя. Ключ «только чтение» не выполняет финансовые изменения, включая создание платежей по расписанию. Действия ключом сохраняют его api_key_id в событии; исходящие веб-хуки используют общие настройки доставки.
| Сущность | События | Что содержит снимок |
|---|---|---|
FinanceOperation | finance_operation.createfinance_operation.updatefinance_operation.delete | Денежная или неденежная операция: сумма, валюта, состояние, собственные распределения allocations и начисления charges, свободный остаток available_credit, сумма возвратов refunded_amount и связи замен replacement_ids. |
FinanceObligation | finance_obligation.createfinance_obligation.update | Ожидаемый платёж, долг или расходное обязательство: сумма, срок, состояние, оплачено paid, списано written_off, остаток remaining, распределения и история начислений, сроков и состояний. |
FinanceRecurring | finance_recurring.createfinance_recurring.update | Расписание: recurrence_start, recurrence_rule, исключения recurrence_exdates, пауза paused и курсор обработки next_on. Курсор отличается от ближайшей даты платежа next_payment_on в ответе API. |
FinanceCategory | finance_category.createfinance_category.update | Категория с действующими для организации названием, направлением, типом и признаком архива. У системной категории сохраняется стабильный логический id. |
FinanceMethod | finance_method.createfinance_method.update | Способ оплаты с действующим для организации названием и признаком архива. У системного способа сохраняется стабильный логический id. |
before — состояние до первого изменения, after — итоговое состояние после всего действия. При создании нет before, при исчезновении объекта нет after. Для update поле changed содержит пары before/after изменившихся полей. Массивы allocations, charges, terms, states и другие JSON-массивы передаются целиком.
В финансовых снимках суммы — точные десятичные строки с двумя знаками, например "100.00" или "-40.00". Не переводите их в двоичные дробные числа для расчётов. Идентификаторы и version — числа, флаги — логические значения, schema_version=1 — версия формата снимка. Поле timezone задаёт часовой пояс дат и времени, отсутствующая nullable-дата передаётся как JSON null.
cause указывает исходное действие: например, save_operation, allocate, edit_operation, correct_operation, save_recurring или sync_regular. Изменения посещения используют booking_sync, booking_complete, booking_cancel, booking_delete, booking_restore. Финансовое изменение цены сохраняет cause=save_booking_price; правка цены через API записи — cause=price_change. Связанное событие booking.update при изменении цены также использует cause=price_change. Одно действие может создать несколько событий по разным затронутым сущностям.
| Действие | События и результат |
|---|---|
| Оплата части ожидаемого платежа | finance_operation.create сообщает об оплате, finance_obligation.update — о том, сколько уже оплачено и сколько осталось оплатить. Использованный прежний аванс также может получить finance_operation.update с новым available_credit. |
| Изменение даты, суммы или правила расписания | finance_recurring.update и finance_obligation.update для пересчитанных автоматических платежей; новые периоды дают finance_obligation.create. Прошлые платежи, оплаты, ручные условия и отмены пользователя защищены. Перенос начала назад достраивает недостающие периоды по новому правилу. |
| Пауза и возобновление расписания | finance_recurring.update меняет paused. Будущие автоматические неоплаченные платежи без ручных изменений получают finance_obligation.update с state=cancelled. Оплаты, ручные условия и отмены пользователя сохраняются. Возобновление восстанавливает допустимые даты с прежними ID через finance_obligation.update; новые допустимые периоды дают finance_obligation.create. Полные календарные дни строго между приостановкой и возобновлением не начисляются, даже при переносе начала назад. Граничные дни сохраняются в часовом поясе организации: короткое выключение и включение не пропускает платёж. |
| Исключение даты или пропуск платежа | Исключённый автоматический период получает finance_obligation.update со state=cancelled, разрешённый пропуск пользователя — со state=skipped. Это не delete. Возврат исключённой даты может восстановить прежний ID автоматического платежа. |
| Отмена операции или удаление посещения | Отмена операции даёт finance_operation.delete: в before остаётся прежняя операция, в after — state=cancelled, история сохраняется. Удаление посещения само по себе не означает удаление финансовой истории: изменение связанного расчёта приходит отдельно, например finance_obligation.update с cause=booking_delete. |
| Редактирование или замена операции | Редактирование с сохранением ID даёт finance_operation.update. Замена даёт update прежнего ID со state=replaced и create новых операций; их ID перечислены в replacement_ids. Связанные источники и обязательства получают собственные итоговые события. |
| Возврат денег | Возврат создаёт расходную finance_operation.create и обновляет затронутые расчёты и исходную оплату. Если возврат меняет стоимость посещения, дополнительно приходит booking.update с cause=price_change. Исправление возврата публикует итоговые изменения по тем же правилам. |
| Архивирование категории или способа оплаты | finance_category.update или finance_method.update с новым признаком archived. Архивирование не является удалением и не стирает прежние операции. |
Сокращённый пример одного события об ожидаемом платеже на 100.00 RUB после оплаты 40.00 RUB. Показаны изменившиеся поля и распределение; полный снимок также содержит начисления, историю сроков и состояний. Создание самой оплаты приходит отдельным событием finance_operation.create.
{
"id": 12347,
"event": "finance_obligation.update",
"type": "update",
"entity": "FinanceObligation",
"entity_id": 401,
"organization_id": 12,
"user_id": 5,
"api_key_id": null,
"created_at": "2026-10-29 12:30:00",
"cause": "save_operation",
"before": {
"id": 401,
"organization_id": 12,
"client_id": 77,
"client_name": "Анна",
"booking_id": 0,
"kind": "manual",
"direction": "income",
"title": "Оплата услуги",
"amount": "100.00",
"currency": "RUB",
"state": "open",
"version": 1,
"due_at": "2026-10-29 00:00:00",
"paid": "0.00",
"written_off": "0.00",
"remaining": "100.00",
"allocations": [],
"timezone": "Europe/Moscow",
"schema_version": 1
},
"after": {
"id": 401,
"organization_id": 12,
"client_id": 77,
"client_name": "Анна",
"booking_id": 0,
"kind": "manual",
"direction": "income",
"title": "Оплата услуги",
"amount": "100.00",
"currency": "RUB",
"state": "open",
"version": 1,
"due_at": "2026-10-29 00:00:00",
"paid": "40.00",
"written_off": "0.00",
"remaining": "60.00",
"allocations": [
{
"id": 901,
"operation_id": 501,
"source_credit_id": 501,
"obligation_id": 401,
"kind": "payment",
"amount": "40.00",
"occurred_at": "2026-10-29 12:30:00",
"reversed_at": null
}
],
"timezone": "Europe/Moscow",
"schema_version": 1
},
"changed": {
"paid": {
"before": "0.00",
"after": "40.00"
},
"remaining": {
"before": "100.00",
"after": "60.00"
},
"allocations": {
"before": [],
"after": [
{
"id": 901,
"operation_id": 501,
"source_credit_id": 501,
"obligation_id": 401,
"kind": "payment",
"amount": "40.00",
"occurred_at": "2026-10-29 12:30:00",
"reversed_at": null
}
]
}
}
}События становятся доступны доставке только после успешного сохранения всего финансового действия (COMMIT). Ошибка записи очереди откатывает и действие, и его события. Повтор успешного запроса с прежним request_id, неизменившийся снимок и откатившийся запрос не создают дополнительных событий.
Успешное сохранение не означает успешную HTTP-доставку. Запросы могут повторяться и приходить в другом порядке: очередь не гарантирует FIFO, в том числе для связанных операций и обязательств. После проверки подписи сохраняйте обработанный X-Nakogda-Delivery и отвечайте повторной доставке без повторного применения изменения. Для дедупликации одного события в нескольких точках доставки используйте верхнеуровневый id payload. Одна version сущности для этого не подходит: остатки и курсор могут измениться без её увеличения.
Изменение цены записи
В данных записи price_totals верхнего уровня содержит полный текущий набор цен по валютам: currency — код валюты, amount — строка с двумя десятичными знаками. Этот массив целиком заменяет прежний набор, включая пустой; добавлять к нему валюты из booking_snapshot не нужно. Числовой price_version — версия текущей цены: устаревшая версия не заменяет более новую. Общая цена доступна без модуля «Финансы». Реальное изменение цены в карточке записи или финансовом редакторе, в том числе возврат со снижением цены и его исправление, создаёт один booking.update с cause=price_change и согласованными состояниями before и after.
В примере цена изменена с 9500.00 на 1500.00 RUB. changed.price_totals передаёт полные массивы стоимости до и после операции, changed.price_version — изменение версии. Правка цены сохраняет исторический booking_snapshot услуг. Последующая замена услуг в календаре не меняет уже согласованный вручную или закреплённый финансовым расчётом набор валют. Если цена ещё не согласована и финансового расчёта нет, календарное изменение услуг обновляет текущую цену. Актуальную стоимость всегда берите из price_totals верхнего уровня.
Сохранение той же цены (no-op) и повтор уже выполненного запроса с тем же request_id (replay) сами по себе не создают новое событие. Внешняя доставка существующего события может повторяться: получатель должен устранять дубликаты по верхнеуровневому id события, а не по entity_id записи.
{
"id": 12346,
"event": "booking.update",
"type": "update",
"entity": "Booking",
"entity_id": 230,
"organization_id": 12,
"user_id": 5,
"api_key_id": null,
"created_at": "2026-10-26 12:30:00",
"created_at_iso_string": "2026-10-26T12:30:00+03:00",
"created_at_timezone": "Europe/Moscow",
"cause": "price_change",
"before": {
"id": 230,
"price_totals": [{ "currency": "RUB", "amount": "9500.00" }],
"price_version": 2,
"booking_snapshot": {
"version": 2,
"price_totals": [{ "currency": "RUB", "amount": "9500.00" }]
}
},
"after": {
"id": 230,
"price_totals": [{ "currency": "RUB", "amount": "1500.00" }],
"price_version": 3,
"booking_snapshot": {
"version": 2,
"price_totals": [{ "currency": "RUB", "amount": "9500.00" }]
}
},
"changed": {
"price_totals": {
"before": [{ "currency": "RUB", "amount": "9500.00" }],
"after": [{ "currency": "RUB", "amount": "1500.00" }]
},
"price_version": { "before": 2, "after": 3 }
}
}Подпись
Если указан секрет подписи, НаКогда добавляет HMAC SHA-256 от исходного тела запроса. Принимающая сторона может пересчитать подпись тем же секретом и сравнить значение.
X-Nakogda-Signature: sha256=<hex_hmac_sha256>
hex_hmac_sha256 = HMAC_SHA256(raw_request_body, signing_secret)Повторы и дубли
Одна доставка не отправляется повторно после успешного HTTP-ответа 2xx. Если внешний сервер не ответил, вернул ошибку или соединение оборвалось, доставка повторяется до 106 раз после первого запроса: через 1, 2, 3, 4, 5 и 10 минут, затем еще 100 раз каждые 10 минут. Для защиты от дублей храните обработанные id или X-Nakogda-Delivery на своей стороне.