Idempotency

Send an Idempotency-Key header so a retried request returns the first result instead of creating a second post or spending a second credit.

Networks time out and automation steps get replayed. Without protection, a retry of POST /v1/posts can create the same post twice. An Idempotency-Key header prevents that.

Which endpoints

EndpointWhat a retry with the same key avoids
POST /v1/postsA second post, a second schedule, a second publish
POST /v1/posts/generateA second draft and a second AI credit
POST /v1/viral/takesSecond drafts and second AI credits

Other endpoints ignore the header. Scheduling and publishing a draft by id are safe to retry without it: a post that is already scheduled or publishing answers 409 not_a_draft instead of going out twice.

How it works

Terminal
curl https://www.postel.app/api/v1/posts \
  -H "Authorization: Bearer $POSTEL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm-sync:deal-4821:announce" \
  -d '{ "platform": "linkedin", "text": "We closed our seed round." }'
  • The first request with a key runs as usual.
  • The same key with the same body, from the same workspace, within 24 hours, gets the first response back. Nothing new is created and no credit is spent. The replayed response has the header Idempotent-Replayed: true.
  • The same key while the first request is still running gets 409 request_in_progress. Wait, then retry with the same key.
  • The same key with a different body gets 422 idempotency_key_reused. Postel compares the raw body, so a retry must send the exact same bytes. Serialize the body once and resend that string.
  • Postel only remembers successful responses. If the first request failed, nothing was created, so a retry with the same key runs again. Fix the request and resend.

Keys are scoped to a workspace and to the endpoint, so the same key on two different endpoints are two separate requests.

Build a good key

A key must be unique per action you want to happen once, and stable across retries of that action. Up to 255 characters.

  • Use an id you already have: crm-sync:deal-4821:announce, n8n:<execution id>:<node>, or a UUID you generate and store before the first attempt.
  • Don't generate a new UUID on every attempt. Then each retry looks new, and the protection is gone.
  • Don't reuse a fixed key for a step that should create something new on every run. A daily "write a post about today's topic" job with a constant key would return yesterday's draft for 24 hours.

On this page