События вебхуков

Справочник событий, которые магазин отправляет в Альбато. Для каждого события указано, какое действие его вызывает и какие поля приходят в запросе. Пути полей нужны при настройке связки: в шагах Альбато поля выбирают из пришедшего примера, например order.total или stock.stockQtyAfter.

Формат запроса

Магазин отправляет каждое событие отдельным запросом POST на адрес вебхука. Тело — JSON, заголовки такие:

  • Content-Typeapplication/json.
  • X-Webhook-Event — код события, например order.created.
  • X-Webhook-Delivery — ID доставки, то же значение, что в поле deliveryId.
  • X-Webhook-Signature — подпись тела: sha256= и HMAC-SHA256 в шестнадцатеричном виде.

В теле всегда есть три служебных поля и объект с данными:

{
  "event": "order.paid",
  "deliveryId": "5b0e7a2c-3f1d-4c8e-9a6b-1d2e3f4a5b6c",
  "timestamp": "2026-09-16T10:05:00Z",
  "order": {
    "id": 123,
    "total": 1175
  }
}
  • event — код события. Используйте его в условии связки;
  • deliveryId — уникальный ID события. При повторной отправке он не меняется, по нему отсеивают дубли;
  • timestamp — время, когда событие поставлено в очередь, в UTC. Это не дата заказа: дату создания берите из order.createdAt;
  • объект с данными называется по первой части кода: order для order.*, client, message, good, stock, performer, absence, booking.

Даты приходят в формате ISO 8601 с часовым поясом, суммы приходят числами, отсутствующие значения — null.

Событие — снимок данных на момент действия. Повторная отправка содержит тот же снимок, даже если данные в магазине уже изменились. Кто выполнил действие, в событии не указано: если связка сама меняет данные через API, магазин пришлёт об этом событие. Не запускайте по событию действие, которое вызовет это же событие, иначе связка зациклится.

Список событий

  • «Новый заказ», order.created — заказ оформлен на сайте, в боте Telegram или MAX, создан в панели или через API.
  • «Изменение статуса заказа», order.status_changed — заказ подтверждён, выполнен или получил новый статус в панели либо через API.
  • «Оплата заказа», order.paid — платёжная система подтвердила оплату или сотрудник отметил заказ оплаченным.
  • «Отмена заказа», order.cancelled — заказ отменён в панели или через API.
  • «Новый клиент», client.created — в магазине появился клиент: из панели, API, сайта или мессенджера.
  • «Изменение клиента», client.updated — карточку клиента изменили в панели или через API, гость зарегистрировался на сайте.
  • «Входящее сообщение», message.received — клиент написал боту в Telegram или MAX.
  • «Новый товар», good.created — товар создан в карточке панели или через API.
  • «Изменение товара», good.updated — карточку товара сохранили в панели или изменили через API.
  • «Удаление товара», good.deleted — товар удалён в панели или через API.
  • «Изменение остатков», stock.changed — остаток изменился: резерв или возврат по заказу, приход или списание.
  • «Малый остаток», stock.low — остаток опустился до порога или ниже.
  • «Новый исполнитель», performer.created — исполнитель добавлен в панели или через API.
  • «Изменение исполнителя», performer.updated — данные исполнителя изменены.
  • «Удаление исполнителя», performer.deleted — исполнитель удалён.
  • «Новое отсутствие исполнителя», absence.created — исполнителю добавлено отсутствие: отпуск, больничный.
  • «Удаление отсутствия», absence.deleted — отсутствие удалено.
  • «Новая запись на услугу», booking.created — клиент записался на услугу на витрине.
  • «Подтверждение записи», booking.confirmed — запись подтверждена в панели или через API.
  • «Завершение записи», booking.completed — запись отмечена завершённой.
  • «Отмена записи», booking.cancelled — запись отменил магазин или клиент.
  • «Неявка клиента», booking.no_show — запись отмечена как неявка.
  • «Перенос записи», booking.rescheduled — клиент перенёс запись на витрине.

Событие отправляется, только если в момент действия услуга подключена, отправка включена и событие отмечено в настройках. Импорт из файла и прямые изменения в базе данных событий не создают. Подробности и исключения — в разделах ниже.

Заказы

Все четыре события о заказах передают одинаковый объект order: клиента, позиции, оплату, доставку и скидки. Дополнительный запрос к API за этими данными не нужен.

Когда отправляются

  • order.created — после успешного оформления на сайте, в боте Telegram или MAX, после создания в панели или через API. Если оформление сорвалось, например не удалось забронировать интервал доставки, события не будет. Заказы Яндекс Маркета и заказы, созданные записью на услугу, это событие не отправляют: для записей есть booking.created.
  • order.status_changed — когда заказ подтверждают, переводят в статус «Новый», «В обработке» или «Доставляется» и когда выполняют. Автоматический перевод в «В обработке» после оплаты в Telegram отправляет только order.paid.
  • order.paid — когда оплату подтвердили ЮKassa, Яндекс Пэй или Telegram или когда сотрудник поставил отметку об оплате. Снятие отметки события не создаёт.
  • order.cancelled — когда заказ отменяют в панели или через API. Запрос клиента на отмену само событие не вызывает, оно придёт, когда магазин отменит заказ.

Поля заказа

  • order.id — ID заказа.
  • order.status — код статуса: 0 — Новый, 1 — Подтверждён, 2 — В обработке, 4 — Доставляется, 3 — Выполнен, -1 — Отменён.
  • order.total, order.currency — итог заказа и валюта. Скидки уже вычтены, доставка, включённая в заказ, и наценка способа оплаты уже прибавлены.
  • order.source — откуда заказ: web, telegram, max, manual (панель или API).
  • order.createdAt, order.updatedAt — дата создания и последнего изменения заказа.
  • order.contactName, order.contactPhone, order.email — контакты, указанные в заказе. Могут отличаться от профиля клиента; если не заполнены — пустые строки.
  • order.comment — комментарий к заказу.
  • order.storeId, order.clientId — ID магазина и клиента.
  • order.externalId, order.externalSource — ID во внешней системе и её название, если заказ создан через API с этими полями.
  • order.client — профиль клиента: id, name, firstName, lastName, phone, email. null, если клиент не найден.

Позиции — массив order.items, по элементу на позицию. В Альбато его обрабатывают как список: например, добавляют строку в таблицу на каждый товар.

  • id, goodId — ID позиции заказа и ID товара.
  • name, article — название и артикул товара на момент события.
  • type — тип: physical, digital, service.
  • quantity, price, total — количество, цена за единицу из заказа и их произведение до скидок на заказ.
  • currency — валюта позиции.
  • options — выбранные опции: groupId, groupCaption (например «Размер»), valueId, valueCaption («M»), иногда valueImage. Без опций — пустой массив.
  • optionsJson — те же опции одной строкой JSON, без опций — "[]". Удобно, когда в поле Альбато нужна строка.

Оплата — объект order.payment:

  • id, name — ID и название способа оплаты в магазине;
  • method — онлайн-провайдер: yookassa, telegram, yandexpay или null для оплаты при получении;
  • paid и status — признак оплаты и состояние: paid — оплачен, waiting — клиенту выставлена ссылка, unpaid — не оплачен;
  • fee — наценка способа оплаты, уже включённая в итог.

Доставка — объект order.delivery:

  • typeId, type, name — ID, код и название способа доставки.
  • kind — вид доставки: courier — по адресу, pickup — самовывоз, digital — цифровая доставка.
  • provider — служба доставки, например cdek. Для собственных способов магазина — null.
  • price, includedValue — стоимость доставки и часть стоимости, уже включённая в order.total. Если стоимость ещё не рассчитана, price равен null — это не бесплатная доставка.
  • priceStatus — расчёт стоимости: calculated — стоимость известна, pending — магазин рассчитает её после оформления, free — бесплатно.
  • paymentMode, paymentStatus — как оплачивается доставка: included — в составе заказа, separate_invoice — отдельным счётом, cash_on_delivery — при получении. Статус отдельного счёта: not_required, pending, paid или null.
  • pricingType — способ расчёта цены: fixed — известна заранее, calculated_later — рассчитывается после оформления. Для обычной доставки — null.
  • address, entrance, floor, apartment, addressComment — адрес, подъезд, этаж, квартира и комментарий к адресу.
  • addressMode, lat, lng — тип адреса: home_address — адрес клиента, pvz_address — пункт выдачи. Координаты точки.
  • date, slotFrom, slotTo — дата и интервал доставки по местному времени: 2026-09-20, 10:00:00, 12:00:00.
  • pickupPoint — точка самовывоза: id, name, address. Для доставки по адресу — null.

Скидки — объект order.discounts: promo — скидка по промокоду, promoCode — сам промокод или null, referral — скидка по партнёрской программе, bonusPointsUsed — оплачено бонусами, manual — ручная скидка, total — сумма всех скидок. Скидки уже учтены в order.total, вычитать их повторно не нужно.

Дополнительные поля по событиям

  • order.status_changedorder.previousStatus (прежний код статуса) и order.statusChangedAt (время смены);
  • order.cancelledorder.previousStatus и order.cancelledAt;
  • order.paidorder.paid всегда равен true, в order.payment стоят paid: true и status: "paid".

Сокращённый пример «Нового заказа»:

{
  "event": "order.created",
  "deliveryId": "22222222-2222-4222-8222-222222222222",
  "timestamp": "2026-09-15T10:00:01Z",
  "order": {
    "id": 123,
    "status": 0,
    "total": 1175,
    "currency": "RUB",
    "source": "web",
    "contactName": "Тестовый покупатель",
    "contactPhone": "+79990000001",
    "createdAt": "2026-09-15T10:00:00Z",
    "items": [
      {
        "goodId": 10,
        "name": "Футболка",
        "article": "SHIRT-M",
        "quantity": 2,
        "price": 500,
        "total": 1000,
        "optionsJson": "[{\"groupCaption\":\"Размер\",\"valueCaption\":\"M\"}]"
      },
      {
        "goodId": 11,
        "name": "Носки",
        "article": "SOCKS",
        "quantity": 1,
        "price": 250,
        "total": 250,
        "optionsJson": "[]"
      }
    ],
    "payment": {"name": "При получении", "method": null, "paid": false, "status": "unpaid", "fee": 0},
    "delivery": {"name": "Курьер", "kind": "courier", "price": 100, "includedValue": 100, "address": "Тестовая улица, 10", "date": "2026-09-20", "slotFrom": "10:00:00", "slotTo": "12:00:00"},
    "discounts": {"promo": 50, "promoCode": "WELCOME", "referral": 25, "bonusPointsUsed": 50, "manual": 50, "total": 175}
  }
}

Клиенты

Когда отправляются

client.created приходит, когда в магазине появляется клиент:

  • сотрудник создал его в панели или связка — через API;
  • посетитель зарегистрировался на сайте по email и паролю;
  • на сайт пришёл новый посетитель — магазин заводит для него анонимного клиента без контактов;
  • человек впервые написал боту в Telegram или MAX, нажал кнопку бота или «Начать» в MAX;
  • человек впервые открыл мини-приложение магазина в Telegram или MAX.

При входе на сайт кнопкой «Войти через Telegram» событие не отправляется.

⚠️ Важно: «Новый клиент» — не всегда контакт с телефоном. У посетителей сайта и пользователей мессенджеров phone и email пустые, а контакты появляются позже. Если связка создаёт лиды в CRM, добавьте условие: телефон или email заполнены.

client.updated приходит, когда:

  • карточку клиента сохранили в панели или изменили через API — даже если значения не поменялись;
  • гость сайта зарегистрировался: к его карточке добавились email, телефон и пароль;
  • клиент без пароля, например вошедший через Telegram, добавил email и пароль.

Не отправляют событие правка профиля самим клиентом на сайте, контакты, введённые при оформлении заказа, и обновление имени из мессенджера.

Поля клиента

  • client.id, client.storeId — ID клиента и магазина.
  • client.caption — отображаемое имя, строка. У анонимного посетителя сайта — «Анонимный пользователь».
  • client.firstName, client.lastName, client.middleName — имя, фамилия, отчество.
  • client.phone, client.email — телефон и email, могут быть null.
  • client.source — откуда клиент: manual — панель или API, web — сайт, registration — регистрация на сайте, telegram и max — мессенджеры.
  • client.externalId, client.externalSource — ID во внешней системе и её название.
  • client.createdAt, client.updatedAt — дата создания и изменения.

Логин, пароль и служебные данные клиента в событие не попадают.

Входящие сообщения

message.received приходит на каждое новое сообщение клиента боту в Telegram или MAX, включая команду /start и ответы на вопросы сценария. Нажатия кнопок, кнопка «Начать» в MAX, сообщения сотрудников, ответы сценариев и рассылки события не создают. Поэтому связка «клиент написал → ответить» не зациклится на собственном ответе.

  • message.id — ID сообщения.
  • message.clientId — ID клиента. Имя и телефон получите методом GET /api/v2/clients/info?id=….
  • message.platformtelegram или max.
  • message.botId — ID бота магазина, который получил сообщение.
  • message.typetext, photo, video, voice, document, contact, location. В MAX встречаются и другие типы вложений, а пустое сообщение приходит как unknown.
  • message.text — текст сообщения. Для документа — имя файла, для контакта в Telegram — номер телефона, для геопозиции в Telegram — координаты вида 55.751244,37.618423.

Сами файлы и ссылки на них в событие не входят. У фото, видео и голосовых сообщений из Telegram поле text пустое: подпись к медиа не передаётся. В MAX подпись приходит в text.

Товары

События о товарах отправляются, когда карточку товара создают, сохраняют или удаляют в панели или через API. good.updated приходит после каждого сохранения, даже если значения не изменились.

Событий нет при импорте каталога из файла, массовых действиях со списком товаров и изменении остатка: об остатках сообщают отдельные события. Когда удаляют товар с вариантами, good.deleted приходит только для самого товара.

  • good.id — ID товара. В good.deleted это единственное поле.
  • good.storeId — ID магазина.
  • good.caption, good.article — название и артикул.
  • good.otherId — код товара во внешней системе.
  • good.price — цена.
  • good.typephysical, digital, service или category.

Остатки

События об остатках касаются физических товаров и вариантов, у которых включён учёт остатка.

Действие Что происходит с остатком
Оформлен заказ Уменьшается на количество товара: товар резервируется
В заказе увеличили количество Уменьшается на разницу
В заказе уменьшили количество или удалили позицию Увеличивается на разницу
Заказ отменён Резерв возвращается
Приход или списание на вкладке «Склад» либо корректировка через API Меняется на указанное количество

При оплате и выполнении заказа остаток не меняется — товар зарезервирован ещё при оформлении, поэтому событий нет. Не создают событий и остаток, заданный при создании или изменении товара, и импорт каталога.

Поля stock.changed:

  • stock.goodId, stock.article — ID и артикул товара или варианта.
  • stock.delta — изменение: отрицательное — списание или резерв, положительное — приход или возврат.
  • stock.stockQtyBefore, stock.stockQtyAfter — остаток до и после изменения.
  • stock.reason — причина: order_created — резерв по заказу, order_cancelled — возврат по заказу, income — приход, manual_adjust — списание или ручная корректировка, либо значение, переданное через API.
  • stock.movementId — ID записи в истории движений товара.
  • stock.orderId — ID заказа, если изменение связано с заказом, иначе null.

stock.low отправляется вместе с stock.changed, когда у товара задан порог больше нуля и остаток перешёл из состояния «выше порога» в «равен порогу или ниже». Дальнейшие списания ниже порога событие не повторяют; после пополнения выше порога следующее пересечение снова его вызовет. Поля: goodId, caption, article, stockQty (остаток после изменения), lowStockThreshold (порог), а также delta, stockQtyBefore, reason, movementId и orderId.

{
  "event": "stock.changed",
  "deliveryId": "d146d97b-67a2-41f2-9259-b00dbb22daee",
  "timestamp": "2026-09-15T12:00:00.482913Z",
  "stock": {
    "goodId": 42,
    "article": "SKU-42",
    "delta": -2,
    "stockQtyBefore": 10,
    "stockQtyAfter": 8,
    "reason": "order_created",
    "movementId": 1234,
    "orderId": 122
  }
}

💡 Чтобы синхронизировать остатки с учётной системой, записывайте абсолютное значение stockQtyAfter, а не прибавляйте delta: при повторной доставке разница применится дважды. Храните последний movementId по товару и не перезаписывайте остаток событием с меньшим movementId — при повторах события могут прийти не по порядку.

Исполнители и отсутствия

События приходят, когда исполнителя добавляют, изменяют или удаляют, а также когда ему добавляют или удаляют отсутствие. Изменение графика работы, фото, точек и привязки к услугам событий не создаёт.

  • performer.created, performer.updatedperformer.id, performer.storeId, performer.fullName, performer.phone, performer.email. performer.updated приходит после каждого сохранения;
  • performer.deleted — только performer.id;
  • absence.createdabsence.id, absence.performerId, absence.startAt, absence.endAt (начало и конец периода), absence.reason (причина);
  • absence.deletedabsence.id и absence.performerId.

Если при добавлении отсутствия магазин отменил пересекающиеся записи, по каждой из них сначала придёт booking.cancelled, а затем absence.created.

Записи на услуги

Когда отправляются

  • booking.created — клиент записался на услугу на витрине. Событие приходит сразу после оформления, в том числе для записи, которая ждёт предоплаты (status: "pending_payment").
  • booking.confirmed — сотрудник подтвердил запись в панели или связка — через API.
  • booking.completed — запись отмечена завершённой.
  • booking.no_show — запись отмечена как неявка.
  • booking.cancelled — запись отменил магазин (в панели, через API или добавлением отсутствия с отменой записей) или клиент на витрине.
  • booking.rescheduled — клиент перенёс запись на витрине. Перенос создаёт новую запись, и событие описывает её.

⚠️ Важно: часть изменений проходит без событий. Не отправляются: перевод записи из «Ожидает оплаты» в рабочий статус после оплаты, отмена из-за неоплаты или истёкшего срока оплаты, отмена записей вместе с заказом, отмена исходной записи при переносе клиентом. Изменение времени, исполнителя и заметок сотрудником в панели или через API тоже событий не создаёт. Если связке важен итоговый статус, периодически сверяйте записи методом получения записи.

Поля записи

Все шесть событий передают одинаковый объект booking:

  • id, storeId — ID записи и магазина.
  • status — статус записи, значения ниже.
  • serviceMode — режим оказания: on_site — на точке, at_client — с выездом к клиенту, online — онлайн, async — без фиксированного времени, к сроку.
  • goodId, goodName — ID и название услуги.
  • performerId, performerName — ID и имя исполнителя.
  • startAt, endAt — начало и конец. У записей asyncnull.
  • dueBy — срок выполнения для записей async, дата вида 2026-10-01.
  • timezone — часовой пояс исполнителя, иначе точки, иначе UTC.
  • clientId, clientName, clientPhone, clientEmail — ID клиента и контакты. Контакты из формы записи важнее данных профиля.
  • locationId, locationName, address — точка оказания услуги и адрес: адрес выезда или адрес точки.
  • clientNotes — комментарий клиента.
  • orderId, orderCompositionId — ID заказа, созданного записью, и позиции в нём.
  • price, quantity, total, currency — цена из заказа, количество, их произведение и валюта.
  • orderTotal, paid, prepaidAmount, paymentMethod — сумма всего заказа, признак оплаты, внесённая предоплата и онлайн-провайдер оплаты.
  • cancellationReason — причина отмены. Если её нет — пустая строка.
  • createdAt, updatedAt, confirmedAt, completedAt, cancelledAt — время создания, изменения, подтверждения, завершения и отмены.

Служебные заметки исполнителя и ссылки на оплату в событие не попадают.

Значения status:

  • pending_payment — ожидает оплаты;
  • pending_confirmation — ждёт подтверждения магазином;
  • confirmed — подтверждена;
  • completed — услуга оказана;
  • cancelled_by_client и cancelled_by_store — отменена клиентом или магазином;
  • no_show — клиент не пришёл.

В booking.rescheduled есть ещё четыре поля: rescheduledFromBookingId — ID прежней записи, previousStartAt и previousEndAt — прежнее время, previousPerformerId — прежний исполнитель.

{
  "event": "booking.confirmed",
  "deliveryId": "8f2b6c1d-4e5a-4b7c-9d8e-0f1a2b3c4d5e",
  "timestamp": "2026-09-16T10:15:31Z",
  "booking": {
    "id": 501,
    "status": "confirmed",
    "serviceMode": "on_site",
    "goodName": "Стрижка",
    "performerName": "Анна Иванова",
    "startAt": "2026-09-20T07:00:00+00:00",
    "endAt": "2026-09-20T08:00:00+00:00",
    "timezone": "Europe/Moscow",
    "clientName": "Мария",
    "clientPhone": "+79991234567",
    "locationName": "Салон на Ленина",
    "price": 1500,
    "total": 1500,
    "paid": false,
    "cancellationReason": ""
  }
}

Проверка подписи

Подпись нужна, если события принимает ваш сервер. Чтобы проверить запрос:

  1. Возьмите тело запроса ровно в том виде, в каком оно пришло, до разбора JSON.
  2. Вычислите HMAC-SHA256 от тела с ключом — секретом подписи из настроек вебхука — и запишите результат в шестнадцатеричном виде.
  3. Добавьте в начало sha256= и сравните с заголовком X-Webhook-Signature. Если значения не совпали, ответьте кодом 401 и не обрабатывайте запрос.

Пример на PHP:

$body = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
$received = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';

if (!hash_equals($expected, $received)) {
    http_response_code(401);
    exit;
}

Пример на Node.js, где rawBody — тело запроса в виде Buffer:

const crypto = require('crypto');

function isValidSignature(rawBody, header, secret) {
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(header || '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Если разобрать JSON и собрать его заново, порядок полей и пробелы изменятся, и подпись не совпадёт. Считайте подпись только от исходного тела.

Повторы и порядок событий

  • Успешной доставкой считается ответ с кодом от 200 до 299. При другом коде или без ответа за 10 секунд магазин повторит отправку через 5 и через 30 секунд, всего три попытки.
  • Одно событие может прийти дважды, например если получатель обработал запрос, но не успел ответить. Отсеивайте повторы по deliveryId.
  • При повторных попытках порядок событий может нарушиться. Не полагайтесь на то, что «Оплата заказа» придёт позже «Нового заказа»: сверяйте данные по времени и ID.
  • Для нового клиента мессенджера «Входящее сообщение» может прийти раньше «Нового клиента».

Короткие ответы

Чем поле timestamp отличается от даты заказа?

timestamp — время, когда магазин поставил событие в очередь на отправку. Дата создания заказа лежит в order.createdAt, а время смены статуса — в order.statusChangedAt. Для отчётов и таблиц используйте поля заказа: при повторной доставке timestamp останется прежним, но к заказу он не относится.

Почему в «Новом клиенте» нет телефона?

Событие приходит и для анонимных посетителей сайта, и для людей, которые только написали боту: контактов у таких клиентов ещё нет. Телефон и email появятся, когда клиент зарегистрируется, и тогда придёт «Изменение клиента». Контакты из заказа передаются в событиях о заказах.

Как выгрузить каждый товар заказа отдельной строкой?

Используйте массив order.items: в Альбато обработайте его инструментом для списков, и каждый элемент станет отдельной строкой с полями name, quantity, price и total. Общие поля заказа, например order.id и order.createdAt, можно добавить в каждую строку.