Skip to main content

At a glance

Success and retries

A delivery succeeds when your endpoint returns any 2xx status within 10 seconds. Otherwise Blujay retries it automatically, up to 10 attempts in total over about 25 minutes: Each wait varies by up to 20% so retries don’t arrive in bursts, and can be longer when Blujay is under heavy load.

What your response means to Blujay

Every retry carries the same event_id and body, with a fresh signature. If your endpoint is down for longer than the retry window, replay the events you missed once it is back.
To force a redelivery on purpose, for example when your database is unavailable, return 503. The Examples page shows this pattern.

Redirects

If your endpoint answers with a redirect (301, 302, 303, 307 or 308 and a Location header), Blujay follows it, up to 3 redirects per attempt:
  • Blujay sends the same POST, with the full body and signature headers, to each new address. It never turns the request into an empty GET.
  • Each step gets its own 10 seconds.
  • Every address must be public HTTPS. A redirect to http:// or to a private address fails the attempt with WEBHOOK_URL_INVALID or WEBHOOK_URL_UNSAFE. See Where Blujay won’t deliver.
  • Too many redirects (WEBHOOK_REDIRECT_LIMIT_EXCEEDED) or a broken Location (WEBHOOK_REDIRECT_INVALID) isn’t retried, because retrying wouldn’t change the result. Fix the URL, then replay the event.
These codes are attempt error_code values from the deliveries list below, not API error codes; they have no error page. Redirects usually mean the subscription URL is slightly off: example.com instead of www.example.com, or a missing or extra trailing /. To find the address your server really answers on, run this. It prints the final URL after any redirects:
Then update the subscription’s url with PATCH /v1/webhook-subscriptions/{subscription_id} so deliveries skip the redirect.

Inspect delivery attempts

Each row is one attempt, newest first. A failed attempt with next_retry_at will be retried; it isn’t final. Add event_id=evt_abc123 to see every attempt for one event; cursor and limit page the list. You can see the same log in Blujay under API → Webhooks.

Find the event for a message

There is no endpoint that lists events, and no lookup from a message_id to its events. The event_id arrives in the webhook-id header and the event_id field of each delivery, so store it with the event. If you never received the event, page the deliveries list for the time window, which shows each event_id and event_type but not the message, and replay the events in that window; each replay carries the full body.

Replay an event

After you fix your endpoint, send an event again:
Blujay answers 202 Accepted and makes one more delivery attempt, recorded with delivery_mode: "replay". Check the deliveries list for its result.
  • The replay has the original event_id and body, in the version the event was created with.
  • If automatic retries for the event are still running, the replay waits for them to finish first.
  • A replay that fails isn’t retried automatically. Replay it again when you’re ready.
  • The subscription must be active and still include the event’s type and phone number, or you get 409 WEBHOOK_SUBSCRIPTION_INACTIVE or WEBHOOK_PHONE_NUMBER_EXCLUDED.
  • Reuse the same Idempotency-Key only to retry the same replay request. A different key sends the event again.
  • Events stay replayable for at least the full retry window. Blujay doesn’t publish a longer retention guarantee, so replay what you missed as soon as your endpoint is back.
  • Only events Blujay recorded can be replayed. While a subscription is disabled, no events are recorded for it. See Webhooks.

Limitations

  • The retry schedule is fixed. You can’t change the number of attempts or the waits, and Retry-After on a 429 is ignored.
  • Replay is one event at a time, to the subscription that recorded it. There is no bulk replay and no replay to a different subscription.
  • Events from a disabled subscription are never recorded, so they can’t be replayed. Pausing a subscription loses events.
  • There is no events endpoint and no message-to-event lookup. Store event_id with every event you receive.
  • Attempt error_code values are not API error codes. Only the API errors above link to an error page.

Common mistakes

  • Don’t return a redirect. Point the subscription at the final URL; a redirect costs a round trip on every attempt and fails if it leaves public HTTPS.
  • Don’t replay before you fix the endpoint. A replay that fails is not retried.
  • Don’t pause a subscription to buffer events. Return 503 instead, so Blujay retries and keeps the events.

Next steps

Verify signatures

Every attempt is signed again.

Events

Every event type and its payload.