Last updated: 2026-03-033 min read

Idempotency

Idempotency prevents duplicate sends when your system retries a request.

How it works

Include an Idempotency-Key header with each send request. The key is a unique string you generate — typically a UUID tied to the business event.

curl -X POST https://truncus.co/api/v1/emails/send \
  -H "Authorization: Bearer $TRUNCUS_API_KEY" \
  -H "Idempotency-Key: order_12345_invoice" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "user@example.com",
    "from": "billing@yourdomain.com",
    "subject": "Invoice #1234",
    "html": "<p>Your invoice is ready.</p>"
  }'

Behavior

If a request with the same idempotency key has already been processed:

  • The original message_id and status are returned
  • No new email is sent
  • The response is identical to the original

An idempotency key stays unique for as long as the email record is retained — your plan's retention window (30 days on Free, longer on paid plans). Within that window a replayed key always returns the original result; it does not reset after a fixed 24-hour period. This is separate from content-based deduplication, which matches identical message bodies within a 10-minute window.

When to use idempotency

  • Payment confirmations — one email per payment, regardless of retries
  • Webhook handlers — safe to retry on delivery failure
  • Agent workflows — re-runs produce the same result
  • Any send where duplication would affect the recipient

SDK usage

const response = await truncus.emails.send({
  to: 'user@example.com',
  from: 'billing@yourdomain.com',
  subject: 'Invoice #1234',
  html: invoiceHtml,
}, {
  idempotencyKey: `order_${orderId}_invoice`
})

Key design

A good idempotency key encodes the business event, not the request:

// Good: tied to the event
const key = `payment_${paymentId}_receipt`

// Good: stable across retries
const key = `user_${userId}_welcome`

// Bad: changes on retry
const key = crypto.randomUUID()

The Idempotency-Key header is required. A send request without it is rejected with 400 MISSING_IDEMPOTENCY_KEY.

Idempotency | Truncus Manual