ListStream Docs

Agencies and events

Webhooks

Set an endpoint per environment (test and live) in the developer console, and we'll tell you when things happen.

Events

delivery.completed
A delivery has been processed. A delivery that couldn't be processed also sends this, with status: "failed".
connection.approved
An agency approved your connection.
connection.revoked
An agency revoked it.
test.ping
Sent by “Send test event” in the console.

Verifying the signature

Each request carries ListStream-Event-Id (the same on every retry) and ListStream-Signature: t=…,v1=…: an HMAC-SHA256 of <t>.<raw body> with your endpoint secret.

javascript
// Node.js: check a webhook came from ListStream
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, header, secret) {           // header: ListStream-Signature
  const parts = Object.fromEntries(String(header ?? "").split(",").map((p) => p.trim().split("=", 2)));
  const t = Number(parts.t), v1 = parts.v1;
  if (!Number.isFinite(t) || typeof v1 !== "string") return false;
  if (Math.abs(Date.now() / 1000 - t) > 300) return false;          // older than 5 minutes
  const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  return v1.length === expected.length && timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

Answering and retries

  • Answer with any 2xx within 10 seconds. Anything else is retried with growing gaps for up to 3 days.
  • Events may arrive out of order, and occasionally more than once: use the event ID to ignore duplicates.
  • Events from the last 3 days that had no endpoint to go to are sent when you add one.
  • Endpoints must be public https addresses.