Outbound

Replay

First-class replay verbs — by message id, by endpoint + time range, or by custom filter.

Replay is the question "can I make this message happen again, exactly as it would have happened the first time?" — answered with a single call instead of by hand-rolling SQL against an outbox table.

The three replay verbs

postel.outbound.replay(opts) takes one of three shapes, distinguished by which fields are present.

By message id

await postel.outbound.replay({
  messageId:        "msg_2k4n8...",
  freshWebhookId:   false,         // keep the original id so receivers can dedup
});

Re-enqueues a single historical message to every endpoint that originally received it. The receiver sees the same webhook-id — if their dedup window is still open, they correctly skip it; if not, they process it again.

Pass freshWebhookId: true if you want the receiver to treat it as new (for testing receiver behavior, for instance).

By endpoint + time range

await postel.outbound.replay({
  endpointId:        "ep_payments_webhook",
  since:             "2026-05-23T00:00:00Z",
  until:             "2026-05-24T00:00:00Z",
  types:             ["order.created", "order.refunded"],
  replayThroughput:  50,     // messages/sec; throttles to avoid overwhelming the receiver
  freshWebhookId:    false,
});

The canonical operational verb: "customer X's webhook receiver was down from 02:00 to 04:00 last Tuesday; replay everything they should have received."

By predicate

await postel.outbound.replay({
  filter:           (msg) => msg.type === "user.signup" && msg.data.plan === "enterprise",
  replayThroughput: 20,
  freshWebhookId:   false,
});

The escape hatch for replays that can't be expressed as "endpoint + time range."

What replay returns

type ReplayResult = { enqueued: number };

A count of outbox rows created. Each row goes through the normal delivery pipeline — same retries, same circuit breaker, same observability hooks.

reconcile

Distinct from replay: reconcile({ endpointId, since }) finds messages that were never confirmed delivered to the given endpoint — the ids whose latest attempt is in a non-delivered state. Useful as a recurring background job: a healthy endpoint returns an empty page; an unhealthy one surfaces the backlog, which you can feed to replay.

const page = await postel.outbound.reconcile({
  endpointId: "ep_payments_webhook",
  since:      new Date(Date.now() - 86_400_000),
  limit:      500,                    // default 100
});
page.items;       // MessageId[] — oldest-first
page.nextCursor;  // resume token, or null once the backlog is exhausted
console.log(`${page.items.length} messages never confirmed delivered`);

The result is bounded — at most limit ids per call, oldest-first, with nextCursor resuming exactly where the previous page ended — so a reconcile over a long outage never materializes the entire backlog in one array. It is the same { items, nextCursor } envelope every list read uses.

Throughput control

The replayThroughput setting (messages/sec) is per-replay, not global. The worker schedules outbox rows tagged with a replay batch id; the rate limiter applies before the HTTP call. A receiver under load won't be flooded by a 100k-message replay.

On this page