Postato Docs
Guides

Webhooks

Subscribe to post lifecycle events and verify delivery signatures.

Webhooks

Postato pushes events to HTTPS endpoints you register. Use them to react to post delivery, approval decisions, and account status changes without polling.

Register a webhook

curl -X POST https://api.postato.com.br/v1/workspaces/$WORKSPACE_ID/webhooks \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/postato",
    "events": ["post.published", "post.failed"]
  }'

The response includes a secret; store it. You won't see it again. It's the key used to verify incoming signatures.

Signature verification

Every outbound delivery carries:

X-Webhook-Signature: t=1712345678,v1=4a7c9e2f0b1d3a8c...
Content-Type: application/json

The signature is HMAC-SHA256(secret, "{timestamp}.{body}") (Stripe-style). Verify in your handler:

import crypto from 'node:crypto';

function verify(req: Request, secret: string): boolean {
  const header = req.headers['x-webhook-signature'] as string;
  const [tPart, v1Part] = header.split(',');
  const timestamp = tPart.split('=')[1];
  const signature = v1Part.split('=')[1];

  const payload = `${timestamp}.${req.rawBody}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) return false;

  // Reject anything older than 5 minutes to prevent replay.
  const skew = Math.abs(Date.now() / 1000 - Number(timestamp));
  return skew <= 300;
}

Use timingSafeEqual instead of === to avoid side-channel leaks.

Events

EventWhen
post.queuedPost accepted and waiting for delivery.
post.publishedSuccessfully delivered to the target network.
post.failedFinal failure after retries; payload includes error detail.
post.scheduledScheduled post accepted; will fire at scheduledAt.
approval.requestedA post entered an approval gate.
approval.decidedA reviewer approved or rejected.
account.disconnectedOAuth token revoked or refresh failure.

Delivery guarantees

  • At-least-once delivery. Make your handler idempotent.
  • Exponential backoff retries for non-2xx responses, up to ~24 hours.
  • Delivery history is available via GET /webhooks/{id}/deliveries.

Responding

Return 2xx as soon as you've durably accepted the payload. Don't do expensive work synchronously; enqueue and respond, then process on your side. Anything above 5 seconds is considered failed and retried.

Rotating the secret

Delete the webhook and create a new one. There is no in-place secret rotation (this is intentional). Rotating implies a clear switchover window, which you control by running the old endpoint alongside the new one briefly.

On this page