Network issues happen. If a request to POST /messages times out, you need to know whether the message actually sent before deciding to retry. Idempotency keys solve this.
Using an idempotency key
Pass a unique value in the Idempotency-Key header on any POST request:
curl -X POST https://api.pulsewave.dev/v1/messages \
-H "Authorization: Bearer pw_live_8f2k9q3m1n7r5t6y4u2i0o8p" \
-H "Idempotency-Key: order-1042-confirmation" \
-H "Content-Type: application/json" \
-d '{ "channel": "email", "to": "ada@example.com", "from": "orders@acme.com", "subject": "Order confirmed", "text": "..." }'
If you retry the exact same request with the same key, Pulsewave returns the original response instead of sending a second message — even if the first request is still being processed.
Choosing a key
A good idempotency key is unique to the operation, not the HTTP request. Derive it from something in your own system, such as an order ID or a job ID, rather than generating a random value on every attempt — a random value defeats the purpose, since a retry would send a different key.
// Good: stable across retries of the same logical operation
const idempotencyKey = `order-${order.id}-confirmation`;
// Bad: a new key every attempt means retries are no longer safe
const idempotencyKey = crypto.randomUUID();
Behavior
- Keys are scoped to your account and remembered for 24 hours.
- Replaying a key with a different request body returns a
400 — Pulsewave assumes this is a bug, not an intentional retry.
- Idempotency only applies to
POST requests that create something (/messages, /templates, /contacts, /lists, /domains, /api-keys, /webhook-endpoints). GET, PATCH, and DELETE are already safe to retry on their own.
Official SDKs generate and attach an idempotency key automatically for retried requests, so you only need to think about this when calling the HTTP API directly.