Which Routes Are Idempotent
| Route | Anchor | Anchor sourced from | TTL |
|---|---|---|---|
POST /v2/enroll | flowId | Stage-1 POST /v2/enroll/signature response | 24 hours |
POST /v2/portfolios/{id}/withdraw | message.nonce | Stage-1 POST /v2/portfolios/{id}/withdraw/signature response (also surfaced as authorizationId) | 10 minutes |
Replay Semantics
Three outcomes are possible when you re-send a request with an anchor the server has already seen:1. Cached replay — 200/201/202
Same anchor, same body. The original response is returned as if the
operation ran again. No side effects. Safe to retry indefinitely within the
TTL.
2. In-progress replay — 409 API_007
Same anchor, still executing. The first call is in flight and has not yet
committed. Back off and retry.
3. Key conflict — 409 API_008
Same anchor, different body. The server refuses to replay because the
two requests would produce different results. Do not retry with the modified
body. Either:
- Send the original body verbatim.
- Or generate a fresh anchor (for enrollment, re-run stage 1; for withdraw, re-run stage 1 — do not reuse the old nonce).
Retry Rules
Safe to retry
5xxresponses (API_600,API_506). Transient server or verifier issues. Back off and retry with the same anchor and body.API_007. In-progress replay. Back off and retry.- Network errors before a response (connection reset, timeout). Back off and retry with the same anchor. If the server did process the request, the retry collapses into a cached replay.
Do not retry
API_008. The server has rejected this body. Start over.400-class errors. The request is wrong. Fix the input before retrying.API_202(portfolio already exists). Terminal. The user is already enrolled in this strategy.
Consult before retrying
404. The resource was not found. A retry with the same id will hit the same 404. Likely a tenant boundary issue.401/403. Fix auth or scope first.
Anchors in Detail
flowId (enrollment)
- Issued by
POST /v2/enroll/signatureas an opaque string. - Valid for 24 hours from issuance. After that, stage 2 returns
400. - Reserves a pooled KMS agent slot and peeks the user’s next account index.
- Do not generate fresh
flowIds to retry stage 2. Each newflowIdcosts a round trip through stage 1 and may burn an account index on success. Always retry with the originalflowIdunless you have hit a terminal error.
message.nonce (withdrawal)
- A 32-byte
0x-prefixed hex string issued byPOST /v2/portfolios/{id}/withdraw/signature. - Also returned as
authorizationIdat the response envelope level — they are the same value. - Valid for 10 minutes from issuance. After that,
API_216on submit. - The nonce is part of the EIP-712 hash, so it is already cryptographically bound to the authorized withdrawal. The server uses it as both the replay key and the uniqueness guarantee for the onchain transfer.
Worked Example — Safe Retry Loop
message and signature are reused verbatim across attempts. The nonce
inside message is the idempotency anchor, so repeats collapse into cached
replays on the server.
What Is Not Idempotent
POST /v2/strategies— strategy creation. Multiple calls produce multiple strategies. The caller is responsible for deduping.POST /v2/portfolios/{id}/startand/stop— naturally idempotent by state: starting an already-started portfolio is a no-op. Safe to retry, but does not use an explicit anchor.- Read endpoints — always safe to retry, no anchor needed.