Docs/Reference

outbox()

Store events in your database from send(), and deliver them from a worker.

import { outbox } from "@typedhook/sdk";

function outbox<Tx>(
  adapter: OutboxAdapter<Tx>,
  opts?: {
    maxAttempts?: number;
    backoffMs?: (attempts: number) => number;
  },
): OutboxDelivery<Tx>;
const wh = webhooks({
  events,
  subscribers,
  signing: { secret: process.env.WEBHOOK_SECRET! },
  delivery: outbox(prismaOutboxAdapter<Prisma.TransactionClient>(prisma), { maxAttempts: 20 }),
});

Tx comes from the adapter and becomes the type wh.with(tx) accepts.

Options

OptionDefaultWhat it does
maxAttempts20Attempts per row before it's marked dead
backoffMs(n) => Math.min(3_600_000, 30_000 * 2 ** (n - 1))Wait in ms after n failed attempts, starting at 1

The default backoff doubles from 30 seconds and stops growing at an hour:

After failed attempt12345678 and later
Wait30s1m2m4m8m16m32m1h

With 20 attempts, a row that never succeeds is retried for about 13 hours before it's marked dead.

Behavior

  • send() validates the event, resolves subscribers and inserts one row per subscriber. It makes no HTTP requests.
  • When there are no subscribers, nothing is inserted.
  • If the insert fails, send() rejects instead of returning an error, so your transaction rolls back.
  • A worker delivers the rows.

Throws

RangeError when maxAttempts isn't a positive integer.

OutboxAdapter

interface OutboxAdapter<Tx = unknown> {
  insert: (rows: OutboxRow[], tx?: Tx) => Promise<void>;
  claim: (limit: number, now: Date) => Promise<OutboxRow[]>;
  complete: (rowId: string) => Promise<void>;
  retry: (rowId: string, attempts: number, nextAttemptAt: Date, lastError: string) => Promise<void>;
  dead: (rowId: string, attempts: number, lastError: string) => Promise<void>;
}

See Write an adapter for the contract of each function and the OutboxRow fields.

Edit on GitHubLast updated on October 14, 2026

On this page

For coding agents

Every page is available as Markdown. Point your agent at the full docs in one file.