Skip to main content
Pass an Idempotency-Key header on send, and retries within 24 hours replay the original response instead of enqueuing a duplicate. Safe even when your producer code doesn’t know whether the previous request succeeded.

Usage

await q.send("emails", payload, {
  idempotencyKey: "order-9001-attempt-1",
})
Under the hood: a header on the HTTP request.
POST /v1/queues/emails/messages
Authorization: Bearer sk_live_...
Idempotency-Key: order-9001-attempt-1
Content-Type: application/json

{"payload": {...}}

Response on replay

If a message with this key was sent in the last 24 hours, the server returns the ORIGINAL message’s 201 response AND sets a header Idempotency-Replay: true. Status is 200, not 201.
{ "id": "msg_abc123", "enqueued_at": "2026-04-18T00:00:00Z" }
No new message is enqueued. Your consumer sees msg_abc123 exactly once.

Key choice

  • Use a deterministic key derived from business data: order-9001-attempt-1, not a random UUID per call.
  • Keys are scoped per tenant. Two different API keys on the same tenant SHARE the idempotency cache.
  • All SDKs auto-generate a UUIDv4 if you omit the option. That’s fine if you call send exactly once per logical event, but defeats the purpose if the caller retries the whole function.

Outbox pattern

async function placeOrder(req: Request) {
  const order = await db.orders.create({ ... })

  // Idempotent send — if placeOrder is retried, no duplicate email.
  await q.send("emails", {
    template: "order-confirmation",
    order_id: order.id,
  }, {
    idempotencyKey: `order-${order.id}-confirm`,
  })

  return Response.json({ ok: true })
}

vs dedupe_id

Different scope: Idempotency-Key = producer retry safety (“this is the same request”). dedupe_id = SQS-style message dedup within a fifo_group (“the same business event”). See FIFO + dedup.