Agent-readable docs index: /docs/llms.txt. Full docs in one file: /docs/llms-full.txt. Download /docs/docs.zip to grep all markdown files locally.

Prevent duplicate sends and handle retries

Idempotency identifies one logical request. Generate a unique value for each new send and persist it alongside your application's job record. Reuse that value only for an exact retry of the original request.
Idempotency-Key: order-1042-shipped-email-v1

Message request semantics

Single-message and batch sends require a nonblank key of at most 200 characters. Missing or oversized keys return 400 with idempotency_key_required. Convert compares the request payload with the stored digest.
  • Completed, identical request: the original response and HTTP status are replayed.
  • Different payload with the same key: 409 idempotency_conflict.
  • Concurrent unfinished request: 409 idempotency_in_progress.
Any changed send is a new logical request. Recipient, OTP, template, variables, channel, subject, or test flag changes need a new key. Do not reuse a validation key after removing test: true.

Timeout handling

A timeout does not prove the provider rejected the message. Keep the original key and payload, back off, and retry identically. If an outcome remains ambiguous, inspect its existing record and provider events before authorizing a fresh send.
Do not auto-switch channels after uncertainty. A provider can accept the first request before the connection fails, so a new SMS/email/WhatsApp request can send the customer a duplicate notification.

Events and other endpoints

Event intake can deduplicate event_id and/or Idempotency-Key. Reusing either for different event name, contact, or properties returns a conflict. Repeated identical event data returns the existing event with duplicate: true.
List-specific contact upsert supports an optional key. Other endpoints do not acquire the same message-send contract merely because you add the header; use each endpoint's documented semantics.

SMTP differs

SMTP deduplication uses the credential, envelope, and byte-identical MIME, including its Message-ID. Changing those inputs changes the fingerprint. A new intentional message should have a new Message-ID, and SMTP does not provide a universal exactly-once guarantee.
Persist before dispatch. Save the application job's request key and payload before sending, so a process restart can retry the same operation.