Idempotency
Idempotency keys make owner action requests safe to retry.
Idempotency gives a retryable capability run one stable request identity. Use it when a network interruption leaves you unsure whether a live mutation reached Dreamstate, especially for a root action that changes workspace ownership, money, members, keys, credentials, or integrations.
The identity belongs to the execution envelope, not to a guessed capability schema. First search and fetch the capability contract, then send the exact same request and key when retrying. The result envelope and its capability-specific data are described in Envelope and errors.
Choose one request identity
The REST gateway accepts a key in the Idempotency-Key header or in the top-level idempotency_key body field. Both are representations of one value. Sending the same value in both places is valid, but different values are not.
The gateway also checks a nested params.idempotency_key when a request is not delegated. A capability that explicitly declares that parameter owns it. For other capabilities, the gateway creates one accepted execution identity rather than allowing a top-level and nested value to drift apart.
| Argument or header | Type | Required | Meaning | Default |
|---|---|---|---|---|
Idempotency-Key | HTTP header, string | Optional, required for root capabilities | A caller-supplied retry identity. It must contain 1 to 255 characters. | None |
idempotency_key | JSON body, string | Optional | The body form of the same retry identity. If both locations are used, the values must match. | None |
params.idempotency_key | Capability parameter, string | Only when the fetched capability schema declares it | A capability-owned field. Otherwise the gateway moves or rejects it so there is one request identity. | None |
delegate_step_token | JSON body, string | Optional | An externally minted execution binding. It uses a server-minted key and refuses caller keys. | None |
approval_id | JSON body, string | Root capability only | A fresh owner-action authorization identity. It cannot be combined with a delegate step token. | None |
mode | JSON body, enum | For mutating calls | The explicit execution mode for the capability. | None |
max_credits | JSON body, nonnegative number | When the fetched contract says a live credit ceiling is needed | A run-wide credit ceiling beside params, not a capability parameter. | None |
quote_hash | JSON body, string | For a quoted priced live call | The quote minted by a prior dry run. The server binds it to the rerun. | None |
tool_turn_token | JSON body, string | When supplied by tool discovery | A short-lived turn-binding value. It is not a credential and must be passed through unchanged. | None |
Do not generate a second key simply because the first response did not arrive. Reusing the identity is what lets the execution path associate a retry with the same operation. A new key asks the system to treat the request as a separate operation.
A stable request identity matters most when the request asks the service to create a durable run or reach an external provider. The route stores the identity in the canonical execution request, so it is available before the gateway dispatches the fetched capability.
Root actions have an extra security rule
Root capabilities have a separate security boundary from ordinary spending and sending. They can change who owns or can reach the workspace or its money. The route requires a current signed-in owner session, a fresh owner-action authorization identity, and an idempotency key before it can execute a root capability.
API keys do not satisfy the signed-in-session requirement for root authority, even if they have broad scopes. This is why a key can be valid for ordinary catalog work but receive destructive_approval_required for a root capability. See Owner actions for the purpose groups and session rule.
A fresh owner-action authorization id is only valid for a root capability. The route rejects an authorization id on a non-root capability, and it rejects a root capability that omits it. Keep the authorization and request identity in the same intended attempt, then retry with the same key if the connection fails.
Delegated execution uses the server key
Delegate execution is the deliberate exception. A delegate step token binds an external credential caller to a particular approved step. The server mints the idempotency key for that step, so the caller must not add an Idempotency-Key header or body field.
If a delegated request supplies a caller key, the route returns invalid_request and identifies idempotency_key as the issue path. Remove both caller placements, retain the delegate step token, and resend the request. A delegate token bound to another capability is refused rather than being reused for a different capability.
The same route refuses a request that includes both a root approval_id and a delegate_step_token. These are different execution paths. Choose the path that the capability contract and authenticated caller actually use.
Retry the same fetched run request
Architect can keep the retry identity on the intended live mutation while it uses the fetched capability schema. For a direct API caller, add the header to the run endpoint after discovery. The placeholder capability id below is intentional: replace it only with an id returned by the current tool search or tool get response.
The run body contains the capability parameters under params, while execution controls such as mode, max_credits, quote_hash, the retry key, and the turn token sit beside params. Use a quoted live request only with the quote and ceiling returned for the unchanged call.
{
"ok": true,
"data": {
"receipt": {
"capability_id": "<fetched-capability-id>",
"run_id": "00000000-0000-0000-0000-000000000000",
"run_step_id": "00000000-0000-0000-0000-000000000000",
"operation_request_key": "...",
"idempotency_key": "workbook-update-00000000-v1",
"replay_strategy": "durable_receipt"
}
},
"meta": { "trace_id": "00000000-0000-0000-0000-000000000000" }
}Receipt fields vary with the fetched capability output schema, but durable mutation receipts use the capability id, run id, run step id, operation request key, idempotency key, and durable_receipt replay strategy. Keep the JSON identifiers when you need to inspect the resulting run.
When it goes wrong
Handle the machine-readable error code first, then use the repair detail and issue paths to rebuild the request. The gateway uses idempotency_conflict when values disagree and does not select one value silently. That protects a caller from retrying an operation whose intended identity is ambiguous.
| Code | HTTP status | What happened | Repair |
|---|---|---|---|
idempotency_conflict | 400 | The header and body differ, or the top-level and params values differ. | Use one key, or repeat the same value in both locations. The server does not choose a winner. |
invalid_request | 400 | A supplied key is shorter than 1 character or longer than 255 characters. | Send a key within the accepted length range. |
idempotency_key_required | 400 | A root capability is called without a key. | Retry the exact root request with Idempotency-Key. |
destructive_approval_required | 400 or 403 | A root action lacks the required current owner session or its owner-action authorization. | Use the signed-in session path described in Owner actions, then keep the request key stable. |
invalid_request | 400 | Delegate execution receives a caller key. | Send its delegate step token without a header or body idempotency key. |
For a key-length refusal, do not trim or alter a key after the original request may have reached the service. Resolve whether the original request completed through its run or receipt where available. If you must send a new operation, treat it as a new request with a new deliberate key.
For a root-action refusal, return to the signed-in workspace session instead of trying to solve it by adding a broader API key. For a delegated-step refusal, remove the caller key instead of adding an authorization id. The route names these different repair paths so a retry does not cross the wrong security boundary.
A retry identity is not a replacement for the tool-turn token. The token proves the current turn received the capability contract, while the idempotency key identifies a repeat of one execution attempt. Preserve each field for its own purpose and do not use an earlier tool token as a retry key.
Idempotency does not make an arbitrary changed request equivalent to an earlier one. If a caller changes the capability, parameters, recipient, mode, or credit ceiling, it has created a different execution decision. Inspect the original receipt or run before choosing whether the changed request should be a new operation.
The route validates identity placement before it dispatches the capability. This gives a client a structured refusal before a provider-facing effect is attempted when keys conflict, when a root request omits its key, or when a delegate request supplies a caller-owned key.
For ordinary capability schemas, do not add params.idempotency_key merely because a root operation needs a header. The fetched schema determines whether that nested field belongs to the capability. The execution envelope remains the place for the header or top-level execution identity.
A root action is not simply an expensive tool. It changes authority, money, credentials, integrations, ownership, membership, keys, or another owner-controlled workspace boundary. That is why its retry identity works together with the signed-in session and fresh owner-action authorization rather than an API key.
The applicable root approval tier may be fresh owner, workspace admin, or current member for mailbox authorization. The caller should read the returned root contract and use the tier it names. The retry key does not elevate a member or an API client to a tier that the root action does not permit.
A successful retry can return a durable receipt rather than repeating an external effect. Read the receipt's run id and operation request key before issuing another request. Those identifiers are more reliable evidence of the attempted operation than a lost client connection or an ambiguous terminal transcript.
A connection timeout is not proof that the service did not receive the request. Preserve the key, inspect the run or receipt where the fetched contract exposes one, and only then repeat the same request identity. This is particularly important when the capability's action policy reaches an external provider.
The key length rule is one through 255 characters. Pick a stable, readable value that a client can store with the intended operation, but do not put a provider secret or a person’s private data in it. A retry key is an identifier, not an authorization credential and not a location for capability parameters.
A caller can send the same key in the HTTP header and top-level body when a client architecture requires both. The values must be equal. This rule makes intermediaries and direct callers converge on one execution identity without allowing the gateway to silently choose a value that the client did not intend.
When a capability returns a 409-style reconciliation situation after an external provider change, inspect the provider state and the local receipt before retrying. Idempotency helps prevent repeated execution, but it does not replace the application-specific reconciliation described by that capability's output and error contract.
Keep the request body and header alongside the returned trace id in client logs. The trace id is useful for support investigation, while the idempotency key is useful for repeat execution. Neither field should be treated as a substitute for the durable run, workspace object, or provider result identifiers.
Architect chooses an execution identity only after it has fetched the current schema and determined whether the operation mutates or requires idempotency. A user can ask Architect to report the receipt and error code, but the agent should not claim a retry succeeded until it receives the canonical response envelope.
For quote-bound live work, retain the idempotency key while repeating the same priced request with its allowed credit ceiling. The quote rule and retry rule work together: the unchanged capability parameters establish the price decision, while the stable identity establishes that a retry is the same intended execution.
If a client no longer knows whether it is repeating an earlier operation, stop and inspect the run history. Sending a new key is appropriate only when the workspace deliberately wants another operation. Sending a new key because of uncertainty defeats the protection that idempotency supplies.
The route's error envelope includes a machine-readable code, a message, and repair information. Let client code branch on the code, then present the human-readable repair. Do not parse a status message and invent a fallback key, because the route has different repairs for conflict, root security, and delegate execution.