Idempotency

An idempotency key lets you retry a request without performing the same operation twice. This page covers how to send a key, what happens when you reuse one and which idempotency errors are safe to retry.

Idempotency keys matter most on POST /funds/allocations. If the request times out, you have no way to tell whether the allocation was created. Retrying with the same key returns the result of the first attempt instead of creating a second allocation.

Send an idempotency key

Pass the key in the Idempotency-Key header:

Idempotency-Key: 3f1a7c4e-9b2d-4f8a-a1c6-5e7d9b0f2a34

Five endpoints honour the key:

  • POST /quotes
  • POST /quotes/carbon
  • POST /quotes/nature
  • POST /funds/allocations
  • POST /funds/allocations/{id}/reverse

Other endpoints ignore the header. GET requests don't create or change anything, so it has no effect on them either.

📘

Send a key on every quote and funds request

The header is optional, but ekko recommends using it wherever it's honoured. Without one, a retry can perform the same operation a second time.

Retry a request

ekko stores a key only when the operation succeeds. A request that fails leaves the key unused, so you can retry with the same value. The same applies when authentication, authorisation, validation or rate limiting rejects a request before it runs.

When the key has already been stored, ekko doesn't run the operation again. You get the same status code as the first request, with a fresh copy of the resource. ekko doesn't save the original response and replay it, so if the resource has changed since, the response shows the current version.

One field works differently. On a nature quote, contribution.suggestedAmounts is calculated when you ask for the quote rather than stored with it, so a replay returns the values current at the time of the retry.

The Idempotent-Replayed response header tells you which of the two you got: true for a replay, false for a request ekko ran for the first time.

📘

A missing Idempotent-Replayed

If the response has no Idempotent-Replayed header, idempotency didn't apply, either because the request carried no key or because the endpoint doesn't support it. Retrying that request can perform the operation a second time.

Allocations are also keyed on quoteId, so POST /funds/allocations rejects a second allocation against a quote that has already been allocated, with or without a key. Sending a key changes what comes back: ekko returns the original allocation. Without a key, the rejection doesn't include it, so you can't see what the first attempt created.

Choose a key

You generate the key. Any printable ASCII value of 1 to 255 characters works and a UUID is a common choice. Your own reference works too, as long as it's unique to the operation.

Use the same key for every retry of a request and a different key for a different operation. ekko compares the request body as well: reusing a key with a different body returns IDEMPOTENCY_KEY_REUSED instead of a replay.

❗️

Keep personal data out of keys

ekko stores the keys you send. Do not use email addresses or other personal identifiers as key values.

Scope and retention

Keys are scoped to your organisation and expire after one week. After that, the same value starts a new request.

Idempotency errors

StatusCodeWhat it meansRetry
409IDEMPOTENCY_KEY_IN_FLIGHTThe first request with this key is still runningYes, same key, after Retry-After seconds
409IDEMPOTENCY_RESOURCE_GONEThe resource the key created has since been reversed or deletedNo
422IDEMPOTENCY_KEY_REUSEDThe key was used with a different request bodyNo, send a new key
400IDEMPOTENCY_KEY_INVALIDThe key is over 255 characters or contains characters outside printable ASCIINo, send a valid key
503SERVICE_UNAVAILABLEThe idempotency store couldn't be reached, so nothing was performedYes, same key, after Retry-After seconds

Retry only where the table says so. The two 409 codes need different handling, so branch on code rather than on the HTTP status.

See Errors and error handling for the full list of statuses and codes.


Did this page help you?