Skip to the page

Webhooks

Replay deliveries

Send an event to your webhook again, signed afresh, after you've fixed whatever made it fail. One delivery from the dashboard or the API, or every delivery that gave up with a short script.

A replay sends an event to the same webhook again, as a new delivery. Use it after you've fixed a bug or brought a server back: the events that failed in the meantime arrive as if for the first time, and your handler deals with them like any other.

Ravi's appointment app was down for four days while he moved hosts. BlueBot retried every reply his clients sent for 72 hours, then gave up on them. Once the new host answered, Ravi listed the deliveries that gave up and replayed them, and his app caught up on 37 replies without anyone texting twice.

What a replay does

  • It makes a new delivery of the same event to the same webhook, and its replay_of names the delivery it repeats.
  • The event is unchanged: the same id, the same webhook-id header and the same body, with data as it was when the event happened.
  • It's signed afresh with the webhook's current secret and a new webhook-timestamp, so it passes your signature check even weeks later.
  • It goes out within a minute, and if it fails it's retried on the usual schedule.
  • Any delivery from the last 30 days can be replayed, whatever its state: one that gave up, one still retrying, or one that was delivered.
  • A replay to a paused webhook waits until you resume the webhook.

Replay one delivery

In the dashboard, open the webhook in Developers → Webhooks, find the delivery in its list, and press Send again.

With the API, find the delivery's id in the deliveries log, then:

POST/v1/webhooks/wh_1Hq2LxW9cVbN3mKpRtYs0a/deliveries/dlv_1Hq2LxW9cVbN3mKpRtYs0a/replaySend a delivery again →
curl -X POST "https://usebluebot.com/api/v1/webhooks/wh_1Hq2LxW9cVbN3mKpRtYs0a/deliveries/dlv_1Hq2LxW9cVbN3mKpRtYs0a/replay" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY" \
  -H "Idempotency-Key: replay-dlv_1Hq2LxW9cVbN3mKpRtYs0a"
Response202
{
  "id": "dlv_2Bf8QzD4kJmA6nXwEuTr1c",
  "object": "webhook_delivery",
  "webhook_id": "wh_1Hq2LxW9cVbN3mKpRtYs0a",
  "event_id": "evt_1Hq2LxW9cVbN3mKpRtYs0a",
  "event_type": "message.received",
  "test": false,
  "state": "pending",
  "attempts": 0,
  "last_status": null,
  "last_error": null,
  "last_duration_ms": null,
  "next_attempt_at": "2026-11-13T19:41:00.000Z",
  "delivered_at": null,
  "replay_of": "dlv_1Hq2LxW9cVbN3mKpRtYs0a",
  "created_at": "2026-11-13T19:41:00.000Z"
}

The answer is the new delivery, waiting to go. Read it again with the deliveries list to see how it went.

You gotIt meansDo next
202 with state: "pending"The replay is queued and goes within a minute.Watch your handler's logs, or list the webhook's deliveries and look for this id.
404 not_foundThe delivery isn't on that webhook, or it's older than 30 days.Check both ids. For an older event, there's nothing left to send.
409 idempotency_conflictYou used this Idempotency-Key for a different request in the last 24 hours.Use one key per delivery, such as replay-<delivery id>.

The Idempotency-Key matters here: without one, a script that runs twice replays every delivery twice.

Replay everything that gave up

After an outage, list the webhook's deliveries that gave up and replay each one. The SDKs page through the list for you, and the key per delivery means running the script again within 24 hours replays nothing twice.

import BlueBot from '@usebluebot/sdk';

const client = new BlueBot({ apiKey: process.env.BLUEBOT_API_KEY });
const webhookId = 'wh_1Hq2LxW9cVbN3mKpRtYs0a';

let count = 0;
for await (const delivery of client.webhooks.deliveries.list(webhookId, { state: 'gave_up' })) {
  await client.webhooks.deliveries.replay(webhookId, delivery.id, { idempotencyKey: `replay-${delivery.id}` });
  count += 1;
}
console.log(`Replayed ${count} deliveries.`);

Each replay is one request, and a key may make 120 a minute. The SDKs wait and try again when BlueBot answers 429 rate_limited, so a long list takes a few minutes. Rate limits

Replays you send together arrive close together and not always in the order the events happened. If your handler cares about order, see Order isn't guaranteed.

When a replay isn't the right tool

  • The event is older than 30 days. Deliveries and events are kept 30 days, so there's nothing left to replay.
  • You want the events at a different address. A replay only goes to the webhook it was made for. Read the events with GET/v1/events and post them where you need them, or point a new webhook at the new address for what happens from now on.
  • You want today's state, not the event's. data is the record as it was. To act on a text's state now, read the text: GET/v1/messages/{id}.

Next