Skip to content

Idempotency & Retry

batch_id is the idempotency key

  • Every write request (PUT / POST) must include a batch_id (UUID v4).
  • If the same batch_id is sent again, the API returns 200 with "status": "ALREADY_RECEIVED" and writes nothing. The content is not compared — the first request received with that batch_id is the one kept.

So:

Situation batch_id
Network timeout, 5xx, 429 Resend with the same batch_id
400 / 413 and you fixed the content Generate a new batch_id
Correcting data already sent (transaction version + 1, resending a daily close) Generate a new batch_id

Warning

Generate the batch_id once per batch and store it with the batch before the first attempt. If you generate a new one on every retry, a timed-out request that actually succeeded will be stored twice.

Retry strategy

Situation Strategy
429 Wait Retry-After seconds, then retry
500 / 503 / network timeout Exponential backoff: 1, 2, 4, 8, 16… minutes, same batch_id, for up to 24 hours
401 Do not retry. Check the API key; contact HQ if it was changed
400 / 403 / 413 Do not retry. Fix the problem and send with a new batch_id.
for each batch in batches_for(store, business_date):
    batch_id = uuid4()                          # generate once, keep for retries
    loop:
        resp = POST /trade-records (batch_id, batch)
        if resp.status in (200, 202): break
        if resp.status == 401: raise check_api_key()
        if resp.status == 429: sleep(resp.Retry-After); continue
        if resp.status >= 500: sleep(backoff()); continue
        raise fix_required(resp.error)           # 4xx: fix, then use a new batch_id
POST /daily-close (new batch_id, control totals)
sleep(30 min)
GET  /daily-close/{store}/{business_date}       # MISMATCHED: resend missing data, then the daily close

A runnable version is in Sample Client.