Skip to content

Idempotency keys

Networks fail. If a request times out you cannot tell whether the email was accepted. Send an Idempotency-Key header and retry freely: the same key with the same body returns the original response and sends the email once.

idempotent-send.ts
import { SpacePost } from "@spacepost/node";
const spacepost = new SpacePost(process.env.SPACEPOST_API_KEY);
const { data, error } = await spacepost.emails.send(
{ from: "Acme <hello@acme.com>", to: "user@example.com", subject: "Receipt", html: "<p>Thanks</p>" },
{ idempotencyKey: "receipt-order-1042" },
);
console.log(error ?? data);
  • Supported on POST /emails and POST /emails/batch.
  • The key is 1 to 256 characters.
  • Keys are scoped to your team and remembered for 24 hours.
  • Same key and same request body: the stored response is returned (same status, same body), nothing new is sent.
  • Same key and a different body: 409 invalid_idempotent_request.
  • Same key while the first request is still running: 409 concurrent_idempotent_requests. Wait a moment and retry.
  • A key outside 1 to 256 characters: 400 invalid_idempotency_key.
  • Requests that fail are not stored, so you can fix the problem and reuse the key.

“Same body” means the parsed JSON is identical. Changing any field, including scheduled_at: "in 1 hour" (which is evaluated at request time), counts as a different request.

Derive the key from the business event, not from a random value generated at retry time:

Good Bad
receipt-order-1042 crypto.randomUUID() on each attempt
welcome-user-u_1042 Date.now()
digest-2026-w43-chunk-3 The same constant for every email