Webhooks
Add an endpoint under Dashboard → Batches & webhooks and we POST JSON to it when something happens: batch.completed and quota.threshold.
POST https://your-app.example.com/hooks/ogsmith
X-OGsmith-Event: batch.completed
X-OGsmith-Delivery: 6f1c…
X-OGsmith-Signature: t=1787840000,v1=9a3f…
{ "id": "6f1c…", "event": "batch.completed",
"createdAt": "2026-08-27T15:51:02.000Z",
"data": { "batchId": "…", "total": 14, "done": 14, "failed": 0 } }The signing secret is shown once when you add the endpoint. Verify a delivery by recomputing the HMAC over timestamp + "." + body. The timestamp is signed together with the body rather than sent beside it, so nobody can replay a captured request with a fresh timestamp:
import { createHmac, timingSafeEqual } from "crypto";
function verify(header, body, secret) {
const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (!(age < 300)) return false; // reject replays
const expected = createHmac("sha256", secret)
.update(`${parts.t}.${body}`)
.digest("hex");
return timingSafeEqual(Buffer.from(parts.v1, "hex"), Buffer.from(expected, "hex"));
}Reply with any 2xx. We retry a failed delivery with a growing delay, up to five attempts, and record every attempt so you can see what happened. Endpoints must be public addresses: we refuse private, loopback and cloud metadata addresses when you add them, and we don't follow redirects.