Webhooks for Post Events
Subscribe to events to get real-time notifications when posts are published, scheduled, or fail. Webhooks are the right choice when you want to know about state changes without polling the API.
Creating a webhook
Code
Response:
Code
The id is a UUID and the secret is a 64-character hex string returned exactly once on creation. Store the secret. You will need it to verify incoming webhook signatures.
Available events
| Event | When it fires |
|---|---|
post.scheduled | A post moves into the scheduled state |
post.published | A post publishes successfully to at least one platform |
post.failed | A post fails to publish on every selected platform |
These are the only three valid events. Creating or updating a webhook with any other value (including a "*" wildcard) returns 400.
Partial publishes fire post.published, not a separate event. When a post succeeds on some platforms and fails on others, the payload's data.status is "warning" and the per-platform outcomes are in data.targets (each with its own status). Inspect targets to see exactly which platforms succeeded or failed.
Payload shape
Code
data.targets holds one entry per selected platform. native_post_id is the platform's own post ID (present when that platform succeeded); error appears only on a non-successful target. data.status is the overall post status (posted, warning for a partial success, or failed). Always branch on the top-level type before accessing data.
Verifying signatures
Every outbound webhook call is signed with HMAC-SHA256 using your webhook's secret. Each delivery carries these headers:
| Header | Value |
|---|---|
X-OmniSocials-Signature | t=<unix_timestamp>,v1=<hex_signature> |
X-OmniSocials-Timestamp | The same unix timestamp, on its own |
X-OmniSocials-Event | The event type (e.g. post.published) |
X-OmniSocials-Webhook-Id | The webhook's ID |
X-OmniSocials-Delivery | A unique ID for this delivery attempt |
The signature is computed over `${timestamp}.${rawBody}` (the timestamp, a literal dot, then the raw JSON body) — the same scheme Stripe uses. To verify: parse t and v1 out of the header, recompute the HMAC over timestamp.rawBody, and compare against v1. A mismatch means the request is not from OmniSocials. Reject it.
Code
Verify against the raw request body, before any JSON parsing or reserialization. Always compare with a constant-time function like timingSafeEqual. Optionally, reject deliveries whose t timestamp is more than a few minutes old to guard against replay.
Managing webhooks
| Endpoint | Description |
|---|---|
GET /webhooks | List all webhooks for this key |
GET /webhooks/:id | Get a single webhook |
PATCH /webhooks/:id | Update URL or events |
DELETE /webhooks/:id | Delete a webhook |
POST /webhooks/:id/rotate-secret | Rotate the signing secret |
Rotating a secret immediately invalidates the old one. Update your verification code to use the new secret before rotating, or accept a short window of failed verifications.