Redelivery
Manually re-enqueue a webhook event after fixing a receiver bug. Same event.id, dedupe by it.
mb.events.redeliver(eventId) re-enqueues a single delivery for another attempt. Use it after fixing a bug in your receiver, or after dropping an event you didn’t process correctly.
Usage
import { Medblocks } from "@medblocks/connect";
const mb = new Medblocks(process.env.MEDBLOCKS_API_KEY!);
const evt = await mb.events.redeliver("evt_01J9YR9N3X4VZ6P2K5RH7M3LMP");
console.log(evt.attempts, evt.last_status_code, evt.delivered_at);The returned WebhookEventRecord reflects state after the redelivery attempt: updated attempts, delivered_at if it succeeded, last_status_code, last_response_body, and last_redelivered_at.
Same event.id Is Reused
Redelivery does not generate a new event id. Your receiver must dedupe by event.id. Otherwise the redelivery applies whatever side effect the original would have.
import type { WebhookEvent } from "@medblocks/connect";
async function handle(event: WebhookEvent) {
const seen = await store.get(`evt:${event.id}`);
if (seen) return;
await applySideEffect(event);
await store.set(`evt:${event.id}`, "ok", { ttl: 90 * 24 * 60 * 60 });
}90 days covers the longest plausible window. Medblocks’ retry schedule runs ~3.6 days; manual redelivery extends that, but anything beyond 90 days is almost certainly a real bug worth investigating.
Rate Limit
Redelivery is rate-limited to one call per minute per event. A second call within 60 seconds returns MedblocksRateLimitError with the Retry-After header parsed onto error.retryAfter.
import { Medblocks, MedblocksRateLimitError } from "@medblocks/connect";
const mb = new Medblocks(process.env.MEDBLOCKS_API_KEY!);
try {
await mb.events.redeliver(eventId);
} catch (err) {
if (err instanceof MedblocksRateLimitError) {
console.log(`retry after ${err.retryAfter}s`);
} else {
throw err;
}
}For bulk replay after a bad deploy, walk the events list with a sleep between calls. See below.
Finding Events To Redeliver
Use mb.webhooks.listEvents to list deliveries for a specific endpoint, then redeliver the ones you need.
import { Medblocks } from "@medblocks/connect";
const mb = new Medblocks(process.env.MEDBLOCKS_API_KEY!);
const ENDPOINT_ID = "wh_01J9YR9N3X4VZ6P2K5RH7M3LMP";
for await (const evt of mb.webhooks.listEvents(ENDPOINT_ID).autoPagingIterator()) {
if (evt.delivered_at !== null) continue; // already delivered
if (evt.last_status_code !== 500) continue; // only replay the 500s
await mb.events.redeliver(evt.id);
await new Promise((r) => setTimeout(r, 1100)); // respect the 1/min limit
}Filter by whichever fields fit your situation:
delivered_at !== null→ already delivered, skip.next_attempt_at !== null→ still retrying on its own, no need to force.last_status_code→ narrow to the failure mode you fixed.attempts→ events that exhausted are terminal until you redeliver them.
When To Redeliver
- After fixing a bug that caused your receiver to 5xx or 400.
- After reactivating a disabled endpoint, to catch up on events whose retries already exhausted during the outage.
- After a downstream system that depends on your webhook handler comes back up.
When Not To
- During the active retry window. Medblocks will retry on its own. Manual redelivery skips the back-off and burns the per-minute rate limit for no reason.
- For events you simply don’t want to handle. There is no “discard” call; just return 2xx and dedupe-by-id will keep you idempotent.
Errors
| Subclass | When |
|---|---|
MedblocksNotFoundError | The evt_* id does not exist for your organization. |
MedblocksRateLimitError | Already redelivered within the last 60 s. Inspect err.retryAfter. |
Related
- Retries & Auto-Disable. The automatic schedule before you’d reach for redelivery.
- Managing Endpoints · List Recent Deliveries. Find the
evt_*to redeliver. - Reference · Redeliver a webhook event.
