You’ll need
- An API key with
webhooks:writeandwebhooks:read. - A public HTTPS URL. Private, loopback, and metadata IPs are blocked at both create time and every delivery.
1. Create a subscription
Savesecret from the response. Meow returns it once.
Every webhook POST requires an Idempotency-Key header, so a retried
create never leaves you with two subscriptions. Generate a fresh key per
operation: 1-50 printable ASCII characters, no spaces. Reusing one is
rejected with a 400.
Response
data.account_id to route crypto send and deposit events to the correct account.
2. Verify the signature
Three headers come with every delivery:
Sign
f"{webhook-id}.{webhook-timestamp}.{body}" with HMAC-SHA-256. The
HMAC key is the base64-decoded body of your whsec_<base64> secret,
not the raw string. Reject anything more than 5 minutes off. That is the
replay window.
3. Dispatch on payload.status
Event names stay coarse: {resource}.created and {resource}.updated.
The lifecycle stage lives on the payload, so one handler covers the whole
flow:
4. Pick a payload mode
Setpayload_mode per subscription. Change it any time with PATCH.
- snapshot (default)
- thin
data carries the full resource. No follow-up GET needed.5. Handle out-of-order deliveries
Deliveries are not ordered. Retries, redrives, and concurrent workers mean an older state for a resource can land after a newer one. Two fields let you stay correct. Every resource event carries asequence, a counter that increments
once per resource, the same for every subscriber, stable across retries and
redrives. Track the highest sequence you’ve applied per resource and
advance it with a single atomic conditional write, so a stale delivery
can’t overwrite newer state:
sequence is absent on webhook.test and message.attempt.exhausted (they
describe no resource). For everything else, pair it with deduplication on
webhook-id (next section): sequence discards stale states, webhook-id
discards exact duplicates.
6. Retries
Meow retries up to 10 times over ~91 hours.- Each non-zero delay gets up to 20% extra jitter.
Retry-Afteron a 429 or 503 is honored, up to 24 hours.- 5 failures in a row open a circuit breaker on the subscription. New deliveries wait out a cooldown (1 → 30 min) without burning an attempt. A success closes it.
- After 10 failed attempts the delivery becomes
failed_permanent, amessage.attempt.exhaustedevent fires, and your business’s admins get an email that names the endpoint and event type. The subscription stays enabled, so an endpoint that rejects one event type keeps getting the rest. - If no delivery to the subscription succeeds for 3 days, Meow disables it
(
disabled_reason=retry_exhausted) and emails your admins. Deliveries that were still retrying are held, not dropped. Events that happen while the subscription is disabled are not sent to it.
- Fix your endpoint.
- Re-enable the subscription with
PATCH /webhooks/subscriptions/{id}and{"is_enabled": true}. The held deliveries go out again. Re-enabling does not resend deliveries that already failed. - Resend the deliveries that failed with
POST /webhooks/subscriptions/{id}/recover?since=<timestamp>. Setsinceto when your endpoint went down. Every failed delivery created at or after that time restarts its retry schedule. Send anIdempotency-Keyheader. Each delivery keeps itswebhook-id, so you can drop the ones you already processed.
GET /webhooks/deliveries by
status=failed_permanent, subscription_id, or created_after. To replay one
delivery, use POST /webhooks/deliveries/{id}/redrive.
7. Operate
Send a test event
Sends
webhook.test to one subscription. Good for verifying a new
receiver without waiting for real activity.Inspect delivery history
Every attempt with HTTP status and response body excerpt.
Redrive a delivery
Resets the counter and re-queues. Works even after
failed_permanent.Rotate the secret
PATCH with rotate_secret: true. Both old and new secrets sign for
7 days; pick whichever your code recognizes. If the old secret leaked,
also send expire_previous_secret: true. The old secret then stops
signing at once, and your next delivery carries one signature.8. Security checklist
Verify the signature
Verify the signature
Constant-time compare.
== leaks the secret.Reject timestamps older than 5 minutes
Reject timestamps older than 5 minutes
Otherwise a leaked payload can be replayed forever.
Dedupe on webhook-id
Dedupe on webhook-id
Same event can arrive twice (retries, redrives).
webhook-id doesn’t change.Drop stale states with sequence
Drop stale states with sequence
Advance the highest
sequence per resource with an atomic conditional
write so a stale delivery can’t overwrite newer state.
See Handle out-of-order deliveries.See also
- Event catalog: payload shape per event.
- Standard Webhooks: the wire format.
POST /webhooks/subscriptions: full subscription contract.