# Receive webhooks and verify signatures
URL: https://skedcast.com/developers/guides/social-media-webhooks

Every state change on a post, a media asset, an account or an approval sends an outbound event right away. Events are signed and retried, and each carries a stable id so you can de-duplicate. Subscribing beats polling for anything beyond a one-off script.

## Register an endpoint
```bash
curl -X POST https://api.skedcast.com/v1/webhooks \
  -H "Authorization: Bearer sked_live_YOUR_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/skedcast/events",
    "events": ["target.published", "target.failed", "media.ready", "media.failed"]
  }'
```

## The events that matter most for a posting integration
- `target.published` / `target.failed`: the outcome of one destination (per account, per platform).
- `media.ready` / `media.failed`: an uploaded asset finished (or failed) processing.
- `account.reauth_required`: a connected account needs its owner to reconnect.
- `post.canceled`: a pending post was canceled before it published.

## Delivery guarantees
Delivery is at-least-once with exponential-backoff retries. Dedupe on the payload's `id`, which is stable across retries.
SkedCast limits how fast it delivers to one endpoint, so a burst of events from a bulk schedule can arrive a little later instead of all at once. A held event is delivered later, unless your endpoint stays over its limit for more than an hour.

## Verifying the signature
Every delivery carries `X-SkedCast-Signature: t=<unix-seconds>,v1=<hex>[,v1=<hex>…]`. The signature verification guide has the full code.
- Sign `"<t>.<rawBody>"` with HMAC-SHA256 and your endpoint's signing secret, over the exact raw request body.
- Accept the delivery if the hex digest matches any `v1` entry, compared in constant time.
- Reject it if nothing matches or if |now − t| is more than 300 seconds (replay protection).

## FAQ
**Q: What if I can't expose a public HTTPS endpoint?**
A: Poll instead: GET /v1/media/{id} (the ready flag), GET /v1/post-targets (per-destination status) and GET /v1/accounts (health). Everything a webhook tells you is also readable on demand.

**Q: Why might I receive the same event twice?**
A: Delivery is at-least-once. If your endpoint processed an event but SkedCast never saw a 2xx response, it retries. Dedupe on the payload id, which stays the same across retries.
