Idempotency
Safe retries for the calls where it matters most.
Every POST in the Commerce API accepts an Idempotency-Key request header. When you retry a request with the same key inside the idempotency window, the terminal returns the result of the original request — it does not run the transaction a second time. This is the primary tool for surviving network blips at the POS without double-charging the cardholder.
The contract
- Same key, same body, within the window — returns the original response (cached). The transaction is not re-run.
- Same key, different body — returns 409 Conflict. Idempotency keys are bound to the request body they were first seen with.
- Different key, same body — treated as a brand-new transaction. The terminal will run it.
- No key on a POST — the request still works, but you lose the safe-retry guarantee. We strongly recommend you always send one.
Format & lifetime
Keys are arbitrary strings up to 255 characters — UUID v4 is recommended. The terminal stores each key for 24 hours from the time of first use, after which the key is forgotten and may be reused without conflict. There is no penalty for using a fresh key on every request.
POST /v1/transaction/sale HTTP/1.1
Host: terminal:8443
X-Api-Key: $ATLAS_API_KEY
Idempotency-Key: 8e1b8b9c-2a4d-4e9f-9b1c-7e2f8a4c2b3d
Content-Type: application/json
{ "type": "SALE", "amount": 2500, "currency": "NZD" }Generating the key
Generate the key before you make the request, and reuse the same key on every retry of the same logical operation. Generating a fresh key inside your retry loop defeats the purpose.
import os, uuid, time, requests
KEY = str(uuid.uuid4()) # generated ONCE for this logical sale
def make_sale():
return requests.post(
f"{os.environ['BASE_URL']}/v1/transaction/sale",
headers={
"X-Api-Key": os.environ["ATLAS_API_KEY"],
"Idempotency-Key": KEY,
},
json={"type": "SALE", "amount": 2500, "currency": "NZD"},
timeout=90,
)
# Retry on network errors using the SAME key — safe to do.
for attempt in range(3):
try:
r = make_sale()
r.raise_for_status()
print(r.json())
break
except requests.RequestException:
if attempt == 2:
raise
time.sleep(2 ** attempt)Which endpoints support it
Every POST endpoint in the API accepts an Idempotency-Key: sale, pre-auth, pre-auth complete, refund, void, card-read, balance-inquiry, granular initiate, granular complete, abort, display, input, print, and reconciliation. GET requests are inherently idempotent and don't accept the header.
Recommended retry policy
- Network error, no response — retry with the same key. Safe.
- 5xx response — retry with the same key. Safe.
- 408 / 504 timeout — retry with the same key. Safe.
- 4xx response (other than 409) — do not retry. Fix the request.
- Successful 2xx — do not retry. Persist the response.