Webhook payload shapes
The envelope, the signing headers, and the event catalog PostGate publishes — laid out with a clear line between what's confirmed in the Webhooks reference and what you should confirm against a live delivery before you rely on it.
data object, this page says so instead of inventing fields. Use POST /api/webhooks/{id}/test to see a real payload for events where the shape isn't fully spelled out.The envelope — confirmed
Every webhook delivery shares the same top-level shape: id, type, occurred_at, and a resource-specific data object. That's the complete confirmed envelope — the guide doesn't publish a full worked JSON example with every field filled in, so treat the block below as the documented field list, not a literal sample response.
Keep unknown fields when you relay an event through your own pipeline. PostGate can add fields to data for a given event type without warning, and a strict allowlist mapper will silently drop them.
{
"id": "string — stable event ID, also sent as X-PostGate-Event-Id",
"type": "string — one of the event catalog names below",
"occurred_at": "string — when the underlying change happened",
"data": { /* resource-specific — see the event catalog for what's documented per type */ }
}Signing headers — confirmed
PostGate signs <timestamp>.<raw-body> with HMAC-SHA256 using your webhook's signing secret. The digest is sent in X-PostGate-Signature as two comma-separated key/value pairs — t (the signing timestamp) and v1 (the hex-encoded HMAC). The event's stable ID is sent separately as X-PostGate-Event-Id, matching the envelope's id field. Both header names are confirmed in the Webhooks reference and used verbatim in the signature verification walkthrough, which includes a complete working verification example.
t=1785254400,v1=4f7c...9a2eEvent catalog — what's named vs. what's fully specified
You choose a subset of these topics in events[] when you create a subscription. For a few event types the guide also documents the specific fields inside data; for the rest, only the event name and a one-line description are published.
Data fields the guide confirms:
post.insights—post_id,targets[](each withtarget_id,platform,platform_post_id,metrics),captured_at.profile.stats_updated—profile_id,platform,followers,follower_delta,metrics,captured_at.profile.expired—reason: "token_expired".profile.reauth_required—reason, one ofrefresh_failed,revoked, orpublish_auth_error.
profile.expired and profile.reauth_required are also confirmed to fire once per authorization state transition, not on every internal maintenance sweep — a profile that reconnects and later breaks again fires a new event rather than repeating the old one.
Named, but with no documented data field list:
post.draft/post.scheduled— a post was created in that state.post.published/post.partial/post.failed— a post reached a terminal state. The guide doesn't enumeratedatafor these; the delivery lifecycle guide documents the equivalenttargetsarray shape returned byGET /api/posts/{id}, which is a reasonable working assumption for whatdatacarries — but it isn't stated as identical, so verify it against a real delivery before you depend on exact field names.profile.connected/profile.disconnected— a profile was connected or disconnected.profile.expiring— a profile's authorization is nearing expiry.stats.updated— PostGate's generic analytics event, covering both post targets and profiles.comment.created/message.received— inbound engagement arrived.
Subscribe to the events you use
Subscribe only to events your product consumes. The signing secret is returned once at creation — save it immediately.
curl -X POST https://postgate.studio/api/webhooks \
-H "Authorization: Bearer $POSTGATE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.example.com/webhooks/postgate",
"events": ["post.published", "post.partial", "post.failed"]
}'See a real payload instead of guessing
For any event type not covered above, the fastest way to get the exact data shape is to trigger one and look at it — not to infer it from field names in prose. POST /api/webhooks/{id}/test queues a test delivery to your endpoint, and logging the raw request body there (before you build any strict parsing against it) gives you the real, current shape for that event type in your account.
data shape you inferred rather than confirmed is a common source of webhook consumers that silently break the first time PostGate adds a field. Deserialize loosely and read only the fields you actually need until a shape is documented.