Idempotency

A network can fail after Essentio did what you asked but before your program heard the answer. Send an Idempotency-Key with every create and every Action, and a retry with the same key is answered with the first request’s answer instead of creating a second Client, sending a document twice or recording a Payment twice.

Sending a key

The key is a header: any string of 1 to 255 printable characters with no space that names this one request — a UUID, or your own order or job id. Make it before the first attempt and keep it for every retry of that request.

A create that may be retried

curl https://api.essentio.pro/v1/clients \
  -H "Authorization: Bearer $ESSENTIO_API_KEY" \
  -H "Idempotency-Key: order-10427" \
  -H "Content-Type: application/json" \
  -d '{"name": "Northwind Studio", "email": "accounts@northwind.example", "country": "CY"}'

What a key does

A key names one request: the operation its method and path reach, with the ids in its path, its query and its body. The body is compared as data: a JSON body by its values, so the same fields in another order are the same request; a form-encoded body by its field names and values, in any order. A body that is neither — or is sent as JSON and does not parse — is compared by its exact bytes, so it is never the same request as an empty body or another such body.

  • The same key and the same request within 24 hours: the answer is the first request’s, byte for byte, with the header Idempotent-Replayed: true. Nothing is done again.
  • The same key with another request: refused with a 422 of type idempotency. Nothing is done. Use a new key for a new request.
  • A request that was refused or failed keeps no key: only a 2xx answer is kept. Correct the request and send it again under the same key.
  • The same request while the first is still being performed: answered 409 of type idempotency_in_flight at once, and nothing is done. Send it again in a few seconds to read the first one’s answer.
  • A request that died on Essentio’s side before it answered holds its key for at most five minutes; after that the same key is performed again.

A key belongs to the credential that sent it — an API key, or a Connected app: another key of the same Business never meets it. After 24 hours it is forgotten, and the same key starts a new request. A Test business’s reset forgets them all.

A retry of a create that succeeded

HTTP/2 201
Content-Type: application/json
Idempotent-Replayed: true

The operations that take one

Every create and every Action takes an Idempotency-Key:

An update (PATCH) and a delete need none: sent twice, they set the same values again, or find nothing left to delete. An Action sent twice without a key is refused by what the first one did — the document is already issued or cancelled — but a Send sent twice without a key sends twice, and a Payment recorded twice is two Payments: send those with a key.

Retrying with a key

Retry a request whose answer was lost — a timeout or a dropped connection — or that was answered 409 or 5xx, with the same key and the same body. A 429 is retried after Retry-After seconds (Rate limits); any other 4xx would be answered the same, so correct the request first.

Create a Client, retrying safely

key=$(uuidgen)
for attempt in 1 2 3; do
  status=$(curl -s -D headers.txt -o client.json -w '%{http_code}' https://api.essentio.pro/v1/clients \
    -H "Authorization: Bearer $ESSENTIO_API_KEY" \
    -H "Idempotency-Key: $key" \
    -H "Content-Type: application/json" \
    -d '{"name": "Northwind Studio", "email": "accounts@northwind.example", "country": "CY"}' || true)
  case "$status" in
    000|409|5??) sleep $((attempt * 2)) ;; # lost, in flight or failed: the same key again
    *) break ;;
  esac
done
[ "$status" = 000 ] && { echo "Essentio could not be reached: try again later with the same key"; exit 1; }
echo "$status $(jq -r '.id // .error.message' client.json)" \
  "$(grep -qi '^Idempotent-Replayed: true' headers.txt && echo '(replayed)')"

An AI assistant’s tools take the same key as an idempotency_key argument.