Webhooks
v1Let your app know when a post reaches a final state. Verify every event before processing it.
Register an endpoint
Create an HTTPS endpoint from the Webhooks screen or API. Save the signing secret returned on creation; it is only shown once.
const { data: endpoint } = await castrook.webhooks.create({
url: 'https://your-app.com/webhooks/castrook',
description: 'Publishing events',
events: ['post.published', 'post.failed', 'post.partially_failed'],
});
// Store endpoint.secret securely. Do not log it.| Event | When it fires |
|---|---|
post.published | Every destination completed |
post.partially_failed | Delivery ended with mixed results |
post.failed | Every destination failed |
post.canceled | A pending post was canceled |
Verify the raw body
Castrook sends webhook-id, webhook-timestamp, and webhook-signature headers. Signatures use HMAC-SHA256 over id.timestamp.raw_body, with v1= followed by a hex digest.
import { verifyWebhook } from '@castrook/sdk';
export async function POST(request: Request) {
const raw = await request.text();
const id = request.headers.get('webhook-id') ?? '';
const valid = await verifyWebhook({
secret: process.env.CASTROOK_WEBHOOK_SECRET!,
id,
timestamp: request.headers.get('webhook-timestamp') ?? '',
signature: request.headers.get('webhook-signature') ?? '',
body: raw,
});
if (!valid) return new Response('Invalid signature', { status: 401 });
const event = JSON.parse(raw);
// Persist event and deduplicate by id before doing work.
// Queue your application's processing here.
return new Response(null, { status: 204 });
}Verify before parsing or modifying the body. Timestamp tolerance defaults to 300 seconds. A valid signature does not replace deduplication.
Delivery and retries
Acknowledge promptly with a 2xx response after safely accepting the event. Delivery is at least once: the same event may arrive more than once. Store processed webhook IDs to make your handler idempotent.
Temporary failures are retried with bounded backoff. Inspect GET /deliveries or the Webhooks screen for attempts, the last response status, and pending or failed deliveries. Disabling an endpoint stops new delivery to it.