Idempotency
Networks fail. Requests time out, connections drop, proxies eat responses. The safe response is to retry — but retrying a mutating request naively creates duplicates. The Idempotency-Key header tells Aleta that two requests with the same key are the same request, and only the first one should take effect.
When to use one
Supply an Idempotency-Key on every POST, PUT, PATCH, or DELETE request that creates or mutates state. GET is already idempotent at the HTTP layer — keys are ignored.
Our SDKs attach a key to every mutating request automatically. Integrators using raw HTTP should do the same.
The header
POST with idempotency
curl -X POST https://api.aleta.io/v1/accounts \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7a3c8f5e-9d4a-4f2c-b8e1-3d5f7c8a2b9e" \
-d '{ "entity_id": "ent_01J…", "institution": "Nordea", ... }'
Any opaque string up to 255 bytes works. UUIDv4 is a safe default. Don't reuse keys across different requests — reuse is how you end up with "the server thinks I already did that" surprises when you meant to perform a fresh action.
What Aleta remembers
For 24 hours after a request with a given key hits the API, Aleta caches the full response — status code, headers, and body — keyed by (workspace, key).
- Same key + same request body → returns the cached response verbatim.
- Same key + different body → returns
409 Conflict. Pick a new key for a new action. - After 24 hours → the cache is purged and the key is available again.
Caching is scoped per workspace. Keys don't collide across workspaces — sandbox and production use separate namespaces. The cache also survives server failover, so retries after infrastructure hiccups do the right thing.
Retry strategy
Use idempotency keys in combination with exponential backoff:
Retry wrapper
async function post<T>(path: string, body: unknown): Promise<T> {
const key = crypto.randomUUID();
let delay = 500;
for (let attempt = 0; attempt < 5; attempt++) {
const res = await fetch(`https://api.aleta.io${path}`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
"Idempotency-Key": key,
},
body: JSON.stringify(body),
});
// Retry on 5xx and 429; everything else is final.
if (res.status >= 500 || res.status === 429) {
await new Promise((r) => setTimeout(r, delay));
delay = Math.min(delay * 2, 30_000);
continue;
}
return res.json();
}
throw new Error("Exhausted retry budget");
}
The same key across every retry is what makes it safe — the first attempt that actually reaches the server is the one that takes effect; subsequent retries receive the same response without double-writing.
Gotchas
- Don't key on the request body hash. If you retry with a subtly different body (trimmed whitespace, reordered JSON), the hash changes and Aleta sees it as a new request. Generate the key once at the start of your logical operation and keep it constant across retries.
- Don't use keys to dedupe at the application layer. They're scoped to 24 hours. If you need application-level uniqueness (e.g. "this trade was already booked"), use a unique constraint on your own primary key, not the idempotency cache.
- Keys are not a substitute for transactions. Multi-resource operations that must succeed or fail atomically need the batch endpoints, not separate calls with shared idempotency keys.
What's next
- Read about errors to understand which status codes are safe to retry.
- Learn how rate limits interact with retry budgets.