Відстеження

Webhooks доставки з підписами

Встановіть HTTPS endpoint на сторінці Webhooks у консолі. Коли повідомлення досягає фінального статусу, ми надсилаємо один POST із JSON подією на цей endpoint.

Події

typeКоли
message.deliveredПовідомлення було доставлено.
message.failedПовідомлення не вдалося; data.error містить код помилки. Його вартість була повернута.
POST to your endpointJSON
{
  "id": "evt_8F3K2Q9TZ1MXD",
  "type": "message.delivered",
  "created_at": "2026-11-07T09:30:17.902Z",
  "data": {
    "id": "msg_8F3K2Q9TZ1MX",
    "object": "message",
    "batch_id": "bat_4M7Q1X0PK2RS",
    "to": "+14155550123",
    "country": "US",
    "text": "Your Northwind code is 482913. It expires in 10 minutes.",
    "segments": 1,
    "price": "0.00507",
    "currency": "USD",
    "status": "delivered",
    "error": null,
    "reference": "login-7781",
    "created_at": "2026-11-07T09:30:12.418Z",
    "sent_at": "2026-11-07T09:30:13.020Z",
    "done_at": "2026-11-07T09:30:17.902Z"
  }
}

Заголовки та підпис

Кожен виклик містить три заголовки у форматі Standard Webhooks:

ЗаголовокЗначення
webhook-idID події (evt_...). Цей самий ID використовується для кожної спроби повторного виконання однієї події.
webhook-timestampЧас Unix у секундах, коли ця спроба була підписана.
webhook-signaturev1, за яким йде підпис base64 HMAC-SHA256. Може бути перераховано кілька підписів, розділених пробілами: приймайте виклик, якщо збігається будь-який з них.

Для перевірки виклику: візьміть секрет із консолі (він починається з whsec_), декодуйте частину після whsec_ за допомогою base64url, щоб отримати ключ, обчисліть HMAC-SHA256 для webhook-id + "." + webhook-timestamp + "." + raw body, закодуйте його в base64 та порівняйте з підписом за постійний час. Відхиляйте часові мітки, які відрізняються більш ніж на п'ять хвилин від вашого часу. Завжди використовуйте raw body: розібране та повторно серіалізоване JSON-тіло не збігатиметься.

// Express: keep the raw body, check the signature, then answer 2xx quickly
import crypto from "node:crypto";
import express from "express";

const app = express();
const secret = process.env.BULKSMSAPI_WEBHOOK_SECRET; // whsec_...

app.post("/hooks/sms", express.raw({ type: "application/json" }), (req, res) => {
  const id = req.header("webhook-id");
  const ts = req.header("webhook-timestamp");
  const key = Buffer.from(secret.slice("whsec_".length), "base64url");
  const expected = crypto.createHmac("sha256", key).update(`${id}.${ts}.${req.body}`).digest("base64");
  const sent = (req.header("webhook-signature") || "").split(" ").map((s) => s.replace(/^v1,/, ""));
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  if (!fresh || !sent.some((s) => s.length === expected.length && crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected)))) {
    return res.status(401).end();
  }
  const event = JSON.parse(req.body);
  console.log(event.type, event.data.id, event.data.status);
  res.status(204).end();
});
app.listen(3000);

Відповідь та повторні спроби

  • Відповідайте будь-яким статусом 2xx протягом 10 секунд. Виконуйте повільну роботу після відповіді, з черги.
  • Перенаправлення не відстежуються: вкажіть налаштування на кінцеву адресу.
  • Виклик, який закінчується тайм-аутом або повертає будь-який статус, окрім 2xx, повторюється через 30 секунд, 2 хвилини, 10 хвилин, 30 хвилин, 1 годину, 2 години, 4 години, 8 годин, 8 годин: всього 10 спроб, приблизно 24 години з першої спроби.
  • Кожна спроба перерахована на сторінці Webhooks із статусом HTTP та часом, який вона зайняла.

Дублікати та порядок

Подія може надійти двічі, наприклад, коли ваш кінцевий пункт зберіг її, але відповідь була втрачена. Зберігайте webhook-id і пропускайте ідентифікатори, які ви вже обробили. Події для різних повідомлень можуть надходити в будь-якому порядку; читайте data.status і data.done_at, а не покладайтеся на порядок надходження.

Правила endpoint

  • Лише HTTPS, на публічному імені хоста (без літералів IP, localhost або приватних мереж).
  • Секрет можна замінити на сторінці Webhooks у будь-який час; виклики, підписані після цього, використовують новий секрет.
  • Видалення кінцевого пункту зупиняє нові події; повідомлення, які вже завершилися, зберігають свій статус для опитування.
Webhooks доставки з підписами | BulkSMSAPI Docs