POST endpoints support idempotency via the Idempotency-Key HTTP header. Same key + same body within 24 hours returns the cached original response instead of doing the work twice. Same key + a different body returns 409 IDEMPOTENCY_CONFLICT. Set this on every retried POST.
A key is shared by the HTTP API and the MCP server: a write sent over one and retried with the same key over the other replays the first result.
One replay can look different from what the HTTP endpoint returns on its own. When an MCP create_email_design call outlasts its wait, it answers 202 { status: "generating", emailId, runId }, and an HTTP retry of POST /v1/emails with the same key replays that 202 instead of generating a second design. Poll GET /v1/emails/{emailId}/status until it reports ready.
Contract
Why Every Retry Needs It
Stable keys prevent duplicate work when a network error or event redelivery makes a caller repeat aPOST. See API design notes
for the rationale and this page’s contract for the exact behavior.
Replayed Trigger Fires
POST /v1/automations/triggers/{triggerEventId}/fire says when a response is a replay. The first fire answers 202 with status: "triggered". A repeat with the same key answers 200 with status: "replayed" and the original triggerInstanceId and automationRunIds. No new runs start. The MCP fire_trigger_event tool answers a repeat the same way.
The fire takes its key only from the Idempotency-Key header. A body idempotencyKey field is rejected with 400 INVALID_REQUEST.
Recommended Key Patterns
The key MUST be stable per logical operation. Common recipes:
Anti-patterns to avoid:
<uuid()>per call: defeats the purpose; every retry gets a fresh key.Date.now()per call: same as above.<sha256(body)>: vulnerable to partial replays where body bytes change.
Worked Example: Fire a Trigger with Retry
- Reuse the same
idempotencyKeyacross every retry of the same logical operation. The first request wins; the rest return the cached response. - Mint a fresh
idempotencyKeywhen the body genuinely changes. A new email recipient = a new key.
409 IDEMPOTENCY_CONFLICT Envelope
Where It Interacts with Other Contracts
- Rate limits: an idempotent replay counts against the rate-limit window. If you saw a
429on the first attempt, the retry will hit the gate too. HonorRetry-After, don’t burn keys. - Workflow runs:
POST /v1/automations/triggers/{triggerEventId}/firecreates rows inautomationExecutions. An idempotent replay returns the originalautomationRunIds[]underdetailsso polling stays consistent. - Sends:
POST /v1/sendsreserves the send atomically. An idempotent replay returns the originalsendId/runIdand short-circuits before re-reservation. The same design can be sent again under a fresh key, and every such call mints a new send.
SDK Behavior
The official@brew.new/sdk ships idempotency keys automatically on every POST call (auto-generated UUID, scoped to the in-process operation) so you get safe-by-default retries. Override the auto-key by passing { idempotencyKey: 'your-key' } in RequestOptions:
See Also
- Rate limits: pair with idempotency for safe retry loops.
- Errors: full
409 IDEMPOTENCY_CONFLICTenvelope. - Async jobs & polling: for fire / send retries you typically don’t need to call again. Poll the run instead.
Need Help?
Our team is ready to support you at every step of your journey with Brew. Choose the option that works best for you:- Self-Service Tools
- Talk to Our Team
Search Documentation
Type in the “Ask any question” search bar at the top left to instantly find relevant documentation pages.
ChatGPT/Claude Integration
Click “Open in ChatGPT” at the top right of any page to explore it further with ChatGPT or Claude.