Webhooks
Webhooks deliver the event catalogue to HTTPS endpoints you register in the portal (Developers, Webhooks) or through the API (up to 20 per organisation, each with its own secret shown once).
Endpoints
POST /v1/webhooks with the URL, the subscription and optional filters:
{
"url": "https://example.com/postbuddy",
"description": "CRM sync",
"events": ["item.*", "postbatch.completed"],
"filters": { "isTest": false }
}The subscription names event types, families (item.*) or everything (*); filters narrow deliveries to a Postflow, a Postbatch or to live or test items. The response carries secret once. GET /v1/webhooks/{id}/deliveries lists the delivery log with the attempt history and the payload; POST /v1/webhooks/{id}/deliveries/{deliveryId}/replay sends an event again; POST /v1/webhooks/{id}/test sends a webhook.test envelope. A disabled endpoint is re-enabled with PATCH {"active": true}; POST /v1/webhooks/{id}/replay-missed then delivers the subscribed events of the disabled period.
Event feed
Integrations that cannot receive webhooks poll GET /v1/events (newest first, cursor pagination, 30 days of retention, filters on type and a period). The feed uses the same envelope as the deliveries.
Delivery
POST https://example.com/postbuddy
X-Webhook-Id: whd_01J…
X-Webhook-Event: item.ready
X-Webhook-Timestamp: 1790000000
X-Webhook-Signature: v1=3f9a…
Content-Type: application/json
{ "id": "evt_01J…", "type": "item.ready", "version": 1, "createdAt": "…", "organisationId": "org_…", "isTest": false, "data": { "item": { "id": "itm_…", "status": "READY" } } }Answer any 2xx within 10 seconds. Failed deliveries are retried with exponential backoff over 24 hours (1 min, 5 min, 30 min, 2 h, 6 h, 12 h, 24 h); after ten consecutive exhausted events the endpoint is disabled and Developers are notified.
Verify the signature
The signature is v1= plus the hex HMAC-SHA256 of timestamp + "." + rawBody with the endpoint secret; reject timestamps older than five minutes. Always verify on the raw body, before parsing.
import { verifyWebhookSignature } from '@postbuddy/sdk/webhooks';
app.post('/postbuddy', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = verifyWebhookSignature({
secret: process.env.WEBHOOK_SECRET!,
headers: req.headers,
rawBody: req.body,
});
handle(event);
res.sendStatus(204);
} catch {
res.sendStatus(400);
}
});from postbuddy import verify_webhook_signature, WebhookSignatureError
try:
event = verify_webhook_signature(secret, request.headers, request.get_data())
except WebhookSignatureError:
abort(400)Handlers must be idempotent: the same event id can be delivered more than once.