Разработчикам
Вебхуки
Настройка эндпоинта
- Добавьте на сервер https-маршрут, который читает сырое тело запроса и отвечает
2xxв течение 10 секунд. Обработку выполняйте после ответа. - Создайте эндпоинт через API или попросите нашу команду. Сохраните секрет
whsec_из ответа: он показывается один раз. - Проверяйте каждый запрос (ниже), затем удаляйте дубликаты по
idсобытия. - Отправьте себе тестовое событие каждого обрабатываемого типа.
Событие
У всех событий одинаковая оболочка. data содержит поля, свойственные типу.
idstring- Начинается с evt_. Одинаков при каждом повторе и в заголовке webhook-id.
typestring- Одно из событий ниже.
api_versionstring- Версия, в которой записаны данные: версия вашего эндпоинта.
created_atstring- Когда это произошло — может быть раньше, чем вы это получите.
testboolean- true для тестовых событий.
dataobject- ID для получения полного объекта и что изменилось.
POST /hooks/ninjabuy HTTP/1.1
Host: acme-goods.com
Content-Type: application/json
User-Agent: NinjaBuy-Webhooks/1.0
webhook-id: evt_4m2x
webhook-timestamp: 1793685600
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
{
"id": "evt_4m2x",
"type": "order.shipped",
"api_version": "2026-11",
"created_at": "2026-11-03T06:00:00Z",
"test": false,
"data": {
"order": "ord_3n7q",
"external_id": "1012",
"shipment": "shp_5r8w",
"carrier": "YunExpress",
"tracking_number": "YT2630812345678",
"tracking_url": "https://t.17track.net/en#nums=YT2630812345678",
"shipping_charged": { "amount": "134.52", "currency": "CNY" },
"pushed_to_channel": true
}
}События
order.created
Заказ дошёл до нас — из вашего магазина Shopify или через API. Оплачен или awaiting_payment.
{
"id": "evt_1a7c",
"type": "order.created",
"api_version": "2026-11",
"created_at": "2026-11-01T09:14:02Z",
"test": false,
"data": {
"order": "ord_3n7q",
"external_id": "1012",
"source": "shopify",
"status": "buying",
"payment_status": "paid",
"lines": [
{
"line": "lin_9a2c",
"variant": "var_41hz",
"sku": "5187209554",
"quantity": 1
}
]
}
}order.paid
Заказ оплачен с баланса. Срабатывает и для заказов, ждавших пополнения.
{
"id": "evt_1a9f",
"type": "order.paid",
"api_version": "2026-11",
"created_at": "2026-11-01T11:02:40Z",
"test": false,
"data": {
"order": "ord_3n8a",
"external_id": "1013",
"amount": { "amount": "57.00", "currency": "CNY" },
"paid_from": "balance",
"was_awaiting_payment": true
}
}order.bought
Мы заплатили продавцу. Позиции в статусе with_seller, пока не дойдут до нашего склада.
{
"id": "evt_2c4e",
"type": "order.bought",
"api_version": "2026-11",
"created_at": "2026-11-01T13:30:05Z",
"test": false,
"data": {
"order": "ord_3n7q",
"external_id": "1012",
"lines": [
{
"line": "lin_9a2c",
"sku": "5187209554",
"quantity": 1,
"status": "with_seller"
}
]
}
}order.checked
Товары на нашем складе, проверены, с фото. Далее — упаковка.
{
"id": "evt_9w1c",
"type": "order.checked",
"api_version": "2026-11",
"created_at": "2026-11-02T03:20:11Z",
"test": false,
"data": {
"order": "ord_3n7q",
"external_id": "1012",
"fulfillment_order": "fo_7k2m",
"lines": [
{
"line": "lin_9a2c",
"qc": "passed",
"photos": [
"https://media.ninjabuy.com/qc/ord_3n7q/1.webp",
"https://media.ninjabuy.com/qc/ord_3n7q/2.webp"
]
}
]
}
}order.shipped
Посылка отправлена с трекингом; стоимость доставки окончательная. Трекинг есть и у магазина, из которого пришёл заказ.
{
"id": "evt_4m2x",
"type": "order.shipped",
"api_version": "2026-11",
"created_at": "2026-11-03T06:00:00Z",
"test": false,
"data": {
"order": "ord_3n7q",
"external_id": "1012",
"shipment": "shp_5r8w",
"carrier": "YunExpress",
"tracking_number": "YT2630812345678",
"tracking_url": "https://t.17track.net/en#nums=YT2630812345678",
"shipping_charged": { "amount": "134.52", "currency": "CNY" },
"pushed_to_channel": true
}
}product.cost_changed
Продавец изменил цену, и что сделало ваше правило синхронизации: пересчитало цену, поставило на паузу или уведомило.
{
"id": "evt_8q0d",
"type": "product.cost_changed",
"api_version": "2026-11",
"created_at": "2026-11-04T02:10:00Z",
"test": false,
"data": {
"product": "prd_1x0a",
"variant": "var_2b7k",
"sku": "4410923871",
"cost": {
"from": { "amount": "42.00", "currency": "CNY" },
"to": { "amount": "48.00", "currency": "CNY" }
},
"action": "repriced",
"price": {
"from": { "amount": "18.99", "currency": "USD" },
"to": { "amount": "20.99", "currency": "USD" }
},
"rule": "rule_default"
}
}product.out_of_stock
У продавца закончился вариант, а на вашей полке его нет: мы обнулили его на ваших каналах.
{
"id": "evt_2b9n",
"type": "product.out_of_stock",
"api_version": "2026-11",
"created_at": "2026-11-04T02:11:30Z",
"test": false,
"data": {
"product": "prd_8f2k",
"variant": "var_41j0",
"sku": "5187209555",
"warehouse_on_hand": 0,
"channels_updated": [
{
"channel": "shopify",
"shop": "acme-goods.myshopify.com",
"available": 0
}
]
}
}qc.issue
Наша проверка выявила проблему с товаром: не тот цвет или размер, повреждение, недостающая деталь. Выберите до respond_by; без ответа мы удерживаем товар и пишем вам.
{
"id": "evt_6c3h",
"type": "qc.issue",
"api_version": "2026-11",
"created_at": "2026-11-02T03:20:11Z",
"test": false,
"data": {
"order": "ord_3n7q",
"external_id": "1012",
"fulfillment_order": "fo_7k2m",
"line": "lin_9a2c",
"sku": "5187209554",
"issue": "wrong_color",
"note": "Received grey, ordered black.",
"photos": ["https://media.ninjabuy.com/qc/ord_3n7q/3.webp"],
"choices": ["ship_anyway", "exchange", "refund"],
"respond_by": "2026-11-05T03:20:11Z"
}
}Подписи
Доставки подписываются по стандарту Standard Webhooks, так что их проверит библиотека, которую вы, возможно, уже используете. С каждой доставкой приходят три заголовка:
| Заголовок | Что содержит |
|---|---|
| webhook-id | ID события. |
| webhook-timestamp | Когда мы отправили эту попытку, в секундах Unix. |
| webhook-signature | v1, затем запятая и base64 HMAC-SHA256 от id.timestamp.body с вашим секретом. Во время ротации — две подписи через пробел. |
Ключ — это ваш секрет после whsec_, декодированный из base64. Подписывайте сырое тело ровно в том виде, в каком оно пришло; отклоняйте метку времени, отличающуюся больше чем на пять минут, чтобы перехваченный запрос нельзя было повторить.
Проверка доставки
С библиотекой standardwebhooks или несколькими строками собственного кода:
Повторы и порядок
Всё, кроме 2xx в течение 10 секунд, считается ошибкой, и мы повторяем попытку, каждый раз с тем же webhook-id и новыми меткой времени и подписью:
| Попытка | После предыдущей |
|---|---|
| 1 | Сразу |
| 2 | 5 секунд |
| 3 | 5 минут |
| 4 | 30 минут |
| 5 | 2 часа |
| 6 | 5 часов |
| 7 | 10 часов |
| 8 | 10 часов, последняя |
- После последней попытки доставка считается неудачной. Отправьте её снова из списка доставок.
- Эндпоинт, все доставки на который не удавались пять дней, отключается, а вам приходит письмо. При повторном включении отправляется то, что он пропустил за последние три дня.
- События могут приходить дважды и не по порядку. Удаляйте дубликаты по id, а когда порядок важен, запросите объект и доверяйте его статусу, а не статусу события.