Documentation Index
Fetch the complete documentation index at: https://docs.moda.app/llms.txt
Use this file to discover all available pages before exploring further.
Idempotency
POST /v1/tasks accepts an optional idempotency_key in the request body. Reusing the same key returns the existing task instead of creating a duplicate.
When to use
- Scheduled jobs. Cron fires at 9am Monday. Use
idempotency_key: "weekly-deck:2026-W17". If your worker crashes after the POST succeeds but before writing the response, the next retry returns the same task. - Network-timeout retries. Your HTTP client times out mid-POST. Retry with the same key — safe.
- Upstream-triggered flows. A Slack command fires two webhooks on the same action. Use the slack message ID as the key:
idempotency_key: "slack:T01234:123456.789". - Bulk fan-out. 50 prospects × one task each.
idempotency_key: "prospect:{id}:2026-04-weekly"keeps re-runs safe.
How it works
- First call: creates the task, returns the envelope.
- Second call with the same key and identical body: returns the same envelope.
- Second call with the same key but a different body: returns
409 idempotency_conflict.
id matches the original, that’s how you can tell.
Conflict handling
- Use a different
idempotency_keyfor the new body. - Change your code so the body is byte-identical (e.g. canonicalize attachment order, don’t include a timestamp in the prompt).
Key design
- Stable and unique per logical operation. “weekly-deck” alone is bad (reused every week);
"weekly-deck:2026-W17"is good. - Hash complex inputs if the key would otherwise be long:
sha256(prompt + brand_kit_id + format). Deterministic + short. - Scope to your own integration. Keys are per-API-key, so namespacing isn’t strictly required, but
"myapp:weekly-deck:2026-W17"is clearer in debug logs. - TTL. Moda retains idempotency records long enough to cover sensible retry windows — don’t rely on a specific duration. If you’re retrying a week later, use a different key.
Not wired (yet)
Idempotency-KeyHTTP header (Stripe-style). Today,idempotency_keyis a body field. A header-based variant is on the roadmap; when it lands, existing body-field usage continues to work.- Idempotency on other endpoints. Only
POST /v1/tasksacceptsidempotency_keytoday. Other writes (POST /v1/brand-kits,POST /v1/uploads) are not idempotent — retrying may create duplicates.POST /v1/uploadsdoes dedupe by content hash (was_duplicate: true) which gives similar safety without the explicit key.
Webhook idempotency (separate concern)
Webhook receivers should use the event envelope’sid (evt_…) as the dedupe key. That’s about guarding your handler against duplicate deliveries — a different concern from idempotency_key on task creation. See webhooks.md.
Worked example — weekly cron
Common wrong guesses
- Using an
Idempotency-KeyHTTP header. Not supported today. Use the body fieldidempotency_key. - Reusing the same key across different operations. “default” as the key for every task. You’ll just keep getting back the first task you ever created with that key.
- Changing the body and reusing the key.
409 idempotency_conflict. Change one or the other. - Assuming idempotency extends to non-task endpoints.
POST /v1/brand-kitswith the same URL twice creates two separate brand-kit-extract tasks (unless cached). OnlyPOST /v1/taskstakesidempotency_key.
Upstream
docs.moda.app/api/tasks/startTask— parameter referenceerrors.md— the full error envelope includingidempotency_conflict