Idempotent requests
How to retry a POST safely with an Idempotency-Key, which routes need one, and what Gravv returns when a key is reused.
An idempotency key lets you retry a write without running the same operation twice. The key tells Gravv that two requests are attempts at the same operation.
To make an idempotent request, generate a key and send it in the Idempotency-Key header. You can create keys any way you like, but Gravv recommends a UUIDv4 or another high-entropy random string to avoid collisions.
When an idempotency key is required
Every POST request requires an Idempotency-Key, except routes under /v1/webhooks/ and /v1/external-accounts/verify. The check runs before authentication. Gravv never deduplicates GET, PUT, PATCH and DELETE requests, so they don't need a key.
The key must be 1 to 255 printable ASCII characters. A missing or malformed key returns HTTP 400 with one of these messages:
missing idempotency key in request headersidempotency key must be between 1 and 255 charactersidempotency key must contain only ASCII printable characters
Use a different key for each operation. Reuse a key only when retrying the same method, path, and payload:
curl --request POST \
--url https://api.gravv.xyz/v1/customers \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: 7f3c2a1e-5b4d-4c6e-9f8a-2b1c0d9e8f70' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"first_name": "john",
"middle_name": "sample",
"last_name": "doe",
"email": "john.doe@example.com",
"phone": "+14074219876",
"type": "individual",
"gender": "male",
"date_of_birth": "1990-06-15",
"external_id": "individual_12345",
"address": {
"address_line1": "1800 N Pole St",
"address_line2": "Suite 202",
"city": "Orlando",
"postal_code": "32801",
"state": "US-FL",
"country": "US"
}
}
'
Gravv scopes each key to your API key, the method and the path. The same key on a different path, or with a different API key, is a new request. After you replace your API key, a retry with an old idempotency key runs again.
Retry behavior
Gravv stores every response below HTTP 500 for 24 hours, and replays it for a retry with the same key:
| Situation | Result |
|---|---|
| Same key and identical payload after completion | Gravv replays the stored status and body, with the header Idempotency-Replayed: true |
| Same key while the original request is running | 409 with Retry-After: 1 and the code idempotency_in_flight |
| Same key with a different payload | 422 with the code idempotency_key_reused |
The first response was a 5xx |
Gravv stores nothing, so a retry with the same key runs again |
| The request body is over 1 MiB | Gravv does not deduplicate the request, so every retry runs. Base64 encoding makes an upload of a file over about 770 KB that large. Check what the first call stored before you retry such an upload |
| Missing or invalid key | 400 with one of the messages above |
{
"data": null,
"error": {
"code": "idempotency_in_flight",
"message": "A request with the same Idempotency-Key is currently being processed"
}
}
When you receive 409 idempotency_in_flight, wait for the number of seconds in Retry-After and retry with the same key and payload.
Follow these rules:
- Gravv stores errors too. Gravv replays a
400,401,403,404or429for 24 hours. After you correct a rejected request, or after a rate limit, send it with a new key. That is safe, because the rejected request did nothing. - Gravv compares payloads byte for byte. Re-serializing the same JSON with a different key order or spacing counts as a different payload and returns
422. Store the exact body you sent, and resend it. - Gravv never stores some responses, such as a transfer's
409with the codeERROR_CODE_TRANSFER_IN_PROGRESS. Retry those with the same key afterRetry-After. - After a
5xxor a timeout, check before you retry a payment. The operation may have run, so look it up first. List transactions with the sourceaccount_idreturns the newest first. Check those transactions for theclient_referenceyou sent. Send the retry only if it is not there.