Appearance
Webhooks
Subscribe to a publication's progress instead of polling. Webhooks are brand-scoped: an endpoint registered under a brand receives events only for that specific brand.
To configure webhooks, first list your brands and select the brand ID:
sh
# List your brands and pick one (the id looks like brand_…)
curl -s "$API/brands" -H "Authorization: Bearer $SK"
export BRAND="brand_…"Register an endpoint
Register a URL, pick the events you care about, and target the brand-scoped webhooks endpoint:
sh
curl -s -X POST "$API/brands/$BRAND/webhooks" \
-H "Authorization: Bearer $SK" -H "Content-Type: application/json" \
-d '{
"url": "https://your-app.example/hooks/socialized",
"events": ["publication.completed", "publication.target.failed"]
}' | jqThe response includes a signing secret (whsec_…) returned once, like an API key. Store it; you'll need it to verify deliveries. List your endpoints with GET /brands/{brandId}/webhooks (without secrets) and remove one with DELETE /brands/{brandId}/webhooks/{id}. The url must be https.
Events
| Event | Fires when |
|---|---|
publication.target.published | a single target goes live |
publication.target.failed | a single target is rejected by its platform |
publication.target.deleted | a live post is deleted on its platform via the API |
publication.target.removed_remotely | a live post is observed to have been deleted manually on the platform |
publication.completed | a publication reaches a terminal state (done / partial / failed) |
connection.reauth_required | a platform needs a connection reconnected (reconnect to keep publishing) |
Verifying a delivery
Every delivery carries two headers:
X-Socialized-Signature:sha256=<hex>, an HMAC-SHA256 of the raw request body, keyed by your endpoint secret.X-Socialized-Delivery: a stable id for this delivery (the same across retries).
Recompute the signature over the bytes you received and compare. Reject anything that doesn't match, so a forged request can't be mistaken for ours.
ts
import { createHmac, timingSafeEqual } from 'node:crypto';
// header value is "sha256=<hex>", so prefix the digest to match.
const expected = `sha256=${createHmac('sha256', endpointSecret).update(rawBody).digest('hex')}`;
const ok =
signature.length === expected.length &&
timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!ok) return new Response('bad signature', { status: 401 });Compute the HMAC over the exact bytes of the body, before any JSON parsing: re-serializing changes the bytes and breaks the signature. Compare in constant time.
Delivery
Deliveries are retried with backoff until they're accepted, so your handler should be idempotent: the same event may arrive more than once with the same X-Socialized-Delivery id, which you can dedupe on. Respond 2xx quickly and do the work asynchronously; a slow handler is a failed delivery.