Śledzenie

Webhooks dostawy z podpisami

Ustaw HTTPS endpoint na stronie Webhooks w konsoli. Gdy wiadomość osiągnie ostateczny status, wysyłamy jedno żądanie POST ze zdarzeniem JSON na ten endpoint.

Zdarzenia

typeKiedy
message.deliveredWiadomość została dostarczona.
message.failedWiadomość nie została dostarczona; data.error zawiera kod. Jej koszt został zwrócony.
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"
  }
}

Nagłówki i podpis

Każde wywołanie zawiera trzy nagłówki, w formacie Standard Webhooks:

NagłówekWartość
webhook-idIdentyfikator zdarzenia (evt_...). Ten sam identyfikator jest używany przy każdej ponownej próbie jednego zdarzenia.
webhook-timestampCzas Unix w sekundach, kiedy ta próba została podpisana.
webhook-signaturev1, po którym następuje podpis base64 HMAC-SHA256. Może być wymienionych kilka podpisów, oddzielonych spacjami: zaakceptuj wywołanie, gdy którykolwiek z nich pasuje.

Aby sprawdzić wywołanie: pobierz sekret z konsoli (zaczyna się od whsec_), zdekoduj część po whsec_ za pomocą base64url, aby uzyskać klucz, oblicz HMAC-SHA256 dla webhook-id + "." + webhook-timestamp + "." + raw body, zakoduj wynik w base64 i porównaj go z sygnaturą w stałym czasie. Odrzuć znaczniki czasu różniące się o więcej niż pięć minut od Twojego zegara. Zawsze używaj surowego ciała: sparsowane i ponownie zserializowane ciało JSON nie będzie pasować.

// 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);

Odpowiadanie i ponowne próby

  • Odpowiedz z dowolnym statusem 2xx w ciągu 10 sekund. Wykonaj powolną pracę po odpowiedzi, z kolejki.
  • Przekierowania nie są śledzone: ustaw adres docelowy na końcowy adres.
  • Wywołanie, które przekroczy limit czasu lub odpowie czymś innym niż 2xx, jest ponawiane po 30 sekundach, 2 minutach, 10 minutach, 30 minutach, 1 godzinie, 2 godzinach, 4 godzinach, 8 godzinach, 8 godzinach: w sumie 10 prób, około 24 godzin od pierwszej.
  • Każda próba jest wymieniona na stronie Webhooks z statusem HTTP i czasem, jaki zajęła.

Duplikaty i kolejność

Zdarzenie może przyjść dwukrotnie, na przykład gdy twój endpoint je zapisał, ale odpowiedź została utracona. Przechowuj webhook-id i pomiń identyfikatory, które już obsłużyłeś. Zdarzenia dla różnych wiadomości mogą przybywać w dowolnej kolejności; czytaj data.status i data.done_at, zamiast polegać na kolejności przybycia.

Zasady endpointu

  • Tylko HTTPS, na publicznej nazwie hosta (bez literałów IP, localhost lub prywatnych sieci).
  • Sekret można zastąpić na stronie Webhooks w dowolnym momencie; wywołania podpisane po tym czasie używają nowego sekretu.
  • Usunięcie endpointu zatrzymuje nowe zdarzenia; wiadomości, które już się zakończyły, zachowują swój status do odpytywania.
Webhooks dostawy z podpisami | Dokumentacja BulkSMSAPI