Retry requests safely with idempotency keys and recover operations whose outcome you don't know, without duplicates or data loss.
A network error, a timeout or a crash on your side can leave you without a response to a request that moves money. You then don't know whether Infinia created the operation.
This page explains how to retry safely with idempotency keys, and how to use list endpoints to find out what happened to an operation and bring your records in sync.
Idempotency keys
An idempotency key is an identifier of your own that you send when you create an operation. The field name depends on the resource:
| Resource | Field in the request body |
|---|---|
| Payout | originId (required) |
| Internal transfer | idempotency_key |
| Deposit refund | idempotency_key |
| Payin refund | idempotency_key |
If you send a key that your company already used for that resource, the API returns the operation that the first request created instead of creating a second one. This makes it safe to retry after a timeout or a 5xx error.
Payins don't have an idempotency key. Sending the same payin request twice creates two payins. Send your own reference with each payin, and before you retry a payin, look it up by reference as described in Recover an operation.
To use idempotency keys:
- Generate a unique key for the operation, for example a UUID, and save it in your database before you send the request.
- Send it in the idempotency field of the request body.
- If the request fails with a network error or a
5xx, retry with the same key and exponential backoff. - Don't retry
4xxerrors. They mean the request itself has a problem, and retrying with the same key returns the same error.
Save the key before you send the request, not after you receive the response. If your process crashes between sending the request and saving the key, you have no key to recover the operation with.
Recover an operation
If retries run out and you still don't know the outcome, list the resource filtered by your key. List endpoints return the current state of each operation, and they're the authoritative source for it.
Paths are relative to https://app2test.infiniaweb.com/infinia_api in sandbox.
| Resource | Request |
|---|---|
| Payout | GET /v2/payouts/?origin_id={your_key} |
| Payin | GET /v1/payments/?reference={your_reference} |
| Internal transfer | GET /v1/accounts/internal-transfer/?idempotency_key={your_key} |
| Deposit refund | GET /v1/accounts/movements/refund/?idempotency_key={your_key} |
| Payin refund | GET /v1/payments/refund/?idempotency_key={your_key} |
- The response contains a record. The operation was created. The record holds its current status, so update your local record to match it.
- The response is empty. The request never reached Infinia. It's safe to retry or reschedule it.
A retry that arrives while Infinia is still processing the first request with the same key can fail with a 4xx, for example a 400 for payouts or a 409 for internal transfers. When a retry fails with a 4xx, look the operation up by your key before you treat it as failed.
Request lifecycle
Follow this flow for every operation you create:
- Generate your idempotency key and save it with your local record, marked as pending.
- Send the
POSTrequest.2xx: Save the returned ID and status on your local record.5xxor timeout: Retry with exponential backoff and the same key. When retries run out, go to step 3.4xx: Don't retry. Read the error and fix the request.
- If the outcome is still unknown, list the resource by your key and update your local record.
- Wait for the final status in a webhook.
Reconcile pending operations
Network errors and crashes can leave a local record in a non-final status longer than expected. A periodic reconciliation job catches these cases:
- Find the records in your database that are still in a non-final status after your expected processing window, for example 30 minutes.
- For each one, list the resource filtered by its idempotency key.
- If a record comes back, update your local status to match it.
- If nothing comes back, the request never reached Infinia. Decide whether to retry it or mark it as failed, based on your business rules.
Use webhooks with the list endpoints as a fallback
Webhooks deliver status changes in near real time and are the right way to react to them. A webhook can still be missed, for example when your endpoint is unreachable for longer than the retry window or your process restarts. In both cases, use the list endpoints to fill the gap.
You don't need Infinia's operation ID to match a webhook to your records. The body includes the key you sent (originId, reference or idempotency_key, depending on the resource), so you can look the record up by your own identifier.
See Webhooks for signature verification, deduplication with the
X-Idempotency-Keyheader, and the retry schedule.
Summary
| Situation | What to do |
|---|---|
| The request timed out | Retry with the same key and exponential backoff. |
You received a 5xx | Retry with the same key and exponential backoff. |
You received a 4xx | Don't retry. Fix the request. If it was a retry, look the operation up by your key first. |
| Retries ran out and the outcome is unknown | List the resource filtered by your key. |
| A local record stays pending too long | Reconcile it with the list endpoint. |
| A webhook was missed or not delivered | List the resource and update your records. |

