Idempotency
Preview Networks drop responses, CI runners retry, Terraform re-applies. An idempotency key makes those retries safe: send the same key with the same request and managed.dev returns the original result — the job or resource you already created — instead of creating a second one.
The Idempotency-Key header
Section titled “The Idempotency-Key header”Send an Idempotency-Key header on any POST that creates a resource or kicks off a
job. The value is a unique string you choose — a UUID is
the natural choice:
POST /v1/sites/site_01J7…/environments/env_01J8…/components HTTP/1.1Host: api.managed.devAuthorization: Bearer mfk_live_…Forge-Version: 2026-06-23Idempotency-Key: 6f1c2e9a-4b7d-4f0a-9c11-2a8e5d3b1f04Content-Type: application/json
{ "kind": "plugin", "slug": "wordpress-seo", "version": "latest", "activate": true }The platform stores the key and the result it produced for 24 hours. Inside that window, a replay returns the original response verbatim. After it expires, the same key is a fresh request again.
Replay is an echo, not a re-trigger
Section titled “Replay is an echo, not a re-trigger”This is the rule that makes retries safe: a replay echoes the original outcome — it does not re-run the work.
- First request with key
6f1c…→ the plugin-install job is created and returned (202 Accepted). - Any retry with the same key
6f1c…within 24h → the samejob_01J9…is returned, with its current status. The install is not started a second time. - This holds even if the original job has since failed. You get the original, failed job back — not a fresh attempt. To genuinely retry the operation, issue a new request with a new key.
Why it matters for CI and Terraform
Section titled “Why it matters for CI and Terraform”Automated callers retry aggressively, and a retry that double-creates is a real hazard: two preview environments, a plugin installed twice, a duplicated deployment.
- CI pipelines retry on transient network failures. Generate one key per logical step so a retried step reconciles against the first attempt instead of stacking a second.
- The official Terraform provider sends an idempotency key
per planned action, so an interrupted
applyresumes cleanly — the re-applyfinds the original job rather than provisioning a duplicate.
What is and isn’t safe to retry
Section titled “What is and isn’t safe to retry”| Situation | Retry safely? |
|---|---|
Any request, with the same Idempotency-Key, within 24h |
Yes — you get the original result back. |
A GET (or any read) |
Yes — reads have no side effects, key or not. |
A POST without a key |
No — there’s nothing to deduplicate against; you may create twice. |
| The same key after 24h | No — the record has expired; it’s treated as new. |
| The same key with a changed body | No — that’s a conflict, not a retry. Use a new key. |
The takeaway: put an Idempotency-Key on every creating POST, even the ones you
don’t expect to retry. It costs nothing on the happy path and turns an ambiguous
network failure into a safe, deterministic replay.
A worked example
Section titled “A worked example”curl -X POST https://api.managed.dev/v1/sites/site_01J7…/environments \ -H "Authorization: Bearer mfk_live_…" \ -H "Forge-Version: 2026-06-23" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "branch": "feature/checkout", "type": "preview" }'const key = crypto.randomUUID();
const env = await mf.environments.create( "site_01J7…", { branch: "feature/checkout", type: "preview" }, { idempotencyKey: key }, // resend the same key on any retry);key := uuid.NewString()
env, err := client.Environments.Create(ctx, "site_01J7…", &forge.EnvironmentCreateParams{ Branch: "feature/checkout", Type: "preview", }, forge.WithIdempotencyKey(key), // resend on retry)The first call returns 202 Accepted with a job; any replay inside 24h returns that
same job. Tail it to completion as described in
async jobs.