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.
// 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.