🔁 Handling Failures and Recovering Transactions

How to handle network errors, timeouts, and unknown transaction states without duplicates or data loss.

This page explains how to use idempotency keys and List endpoints together to handle retries and recover operations whose outcome is uncertain.


Idempotency keys

Every POST endpoint accepts a unique identifier in the request body. The field name varies by resource:

ResourceField name
PayoutoriginId
Payinreference
Internal Transferidempotency_key

If you submit two requests with the same key, the API returns the result of the first request instead of executing the operation again. This makes it safe to retry after a timeout or a 5xx error without risking a duplicate transaction.

The recommended pattern:

  1. Generate a unique identifier for the operation (e.g. a UUID) and persist it in your database before sending the request
  2. Include it as the idempotency key field in the request body
  3. If the request fails due to a network error or 5xx, retry with the same identifier and exponential backoff
  4. Do not retry on 4xx errors — those indicate a problem with the request itself, and the same key will return the same error
⚠️

Persist the key before you send, not after you receive a response. If your process crashes between sending the request and saving the key, you lose the ability to recover the operation.


Recovering an operation

After exhausting retries, if the outcome of an operation is still uncertain, query the List endpoint filtered by your idempotency key. List endpoints always return up-to-date results and are the authoritative source for the current state of any resource.

ResourceHow to query by key
PayoutGET /v2/payouts/?origin_id={your_key}
PayinGET /v1/payments/?reference={your_key}
Internal TransferGET /v1/internal-transfers/?idempotency_key={your_key}

If the response contains a record, the operation executed — the record includes the current status, so you can retrieve the final outcome directly from the response and sync your local state accordingly. If the response is empty, the operation never reached Infinia and it is safe to retry or reschedule.


Request lifecycle

Every outgoing operation should follow this flow:

  1. Generate and persist your idempotency key locally (mark the record as PENDING)
  2. Send the POST request
    • 2xx → update your local record with the returned ID and status
    • 5xx or timeout → retry with exponential backoff using the same key; after max retries, query the List endpoint (step 3)
    • 4xx → do not retry; inspect the error and fix the request
  3. If the outcome is still unknown, query the List endpoint by your key and sync local state
  4. Await the final status update via webhook

Reconciliation

Network errors and process crashes can occasionally leave records in a non-final state longer than expected. A periodic reconciliation job catches these cases by scanning for operations that have been PENDING beyond a reasonable threshold and querying Infinia to sync them.

Recommended approach:

  1. Query your database for records in a non-final state older than your expected processing window (e.g. 30 minutes)
  2. For each record, call the List endpoint filtered by its idempotency key
  3. If a record is found, update your local state to match its current status
  4. If no record is found, the operation never reached Infinia — decide whether to retry or mark it as permanently failed based on your business rules

Webhooks

Webhooks deliver state changes in near real-time and are the right way to react to events as they happen. Webhook delivery can fail if your endpoint is temporarily unreachable, or you may miss events after a process restart. In both cases, the List endpoint is your fallback — use it to reconcile any gaps.

When processing a webhook, you don't need to use Infinia's resource ID to match it to your internal records. The payload includes the idempotency key you originally submitted (originId, reference, or idempotency_key depending on the resource), so you can look up the corresponding record using your own identifier.

📘

See the Webhooks page for payload verification, deduplication using the X-Idempotency-Key header, and the automatic retry schedule.


Summary

SituationAction
Request timed outRetry with the same key and exponential backoff
Received 5xxRetry with exponential backoff, same key
Received 4xxDo not retry — fix the request
Exhausted retries, outcome unknownQuery the List endpoint filtered by your key
Record stuck in PENDINGReconciliation using the List endpoint
Webhook missed or undeliveredQuery the List endpoint