Idempotency
In payment systems, retries due to network errors are unavoidable. Without idempotency, a retry can create duplicate charges or duplicate accounts. The API offers two complementary mechanisms:
1. Idempotency-Key (recommended)
Any POST, PUT, PATCH or DELETE can carry the Idempotency-Key header with a unique identifier chosen by the integrator (UUID, hash, or any string ≤ 255 characters).
Semantics
| Situation | Response |
|---|---|
| First time with that key | Executes normally. The key + the result are cached for 24 hours. |
Same Idempotency-Key + same request body, within 24h | Returns the same response without re-executing the operation. |
Same Idempotency-Key + different body | 409 idempotency-key-reuse. Almost always indicates an integrator bug. |
| After 24h | The key is reused as new. |
Scope
The idempotency cache is scoped by (apiKeyId, idempotencyKey). This means:
- Two integrators can use the same key without colliding.
- The same integrator cannot reuse the key across API key rotations (not a practical concern).
When mandatory
| Resource | Recommended | Mandatory |
|---|---|---|
POST /v1/clients | Yes | No (see §2, native upsert) |
POST /v1/associates | Yes | No (UNIQUE on taxId + customer) |
POST /v1/receivable-accounts | Yes | Yes (only way to recover the ID if the response is lost before reading it) |
POST /v1/receivables | Yes | Yes (especially for bulk ERP flows) |
POST /v1/webhooks | Yes | No |
PATCH /v1/receivables/{id}/status | Yes | No |
Example
POST /v1/receivables
Authorization: Bearer eyJ...
Idempotency-Key: erp-fac-2026-05-15-00012345
Content-Type: application/json
{
"receivableAccountId": 4242,
"amount": 45000.00,
"currencyCode": "ARS",
"receivableType": "INVOICE",
"legalNumber": "0001-00012345",
"issueDate": "2026-05-15",
"dueDate": "2026-05-30"
}
If the response is lost due to a timeout, resend the same request with the same Idempotency-Key and you will receive the same response without duplicating the receivable.
Recommended policy (How-to)
Generate the Idempotency-Key deterministically from stable ERP identifiers:
erp-{company}-{resource}-{externalId}
erp-distribuidora-demo-fac-202605-00012345
This lets you retry from scratch, without duplication risk within the 24h window. For retries beyond 24h, protection depends on the resource (see §2): some resources are natively idempotent, others require handling 409 conflicts.
If you change the body of a retry (correct an amount, add a field), you must also change the Idempotency-Key. Reusing the same key with a different body returns 409 idempotency-key-reuse.
2. Behavior on business-key collisions
Beyond Idempotency-Key (which protects within 24h), some resources have business keys that also detect duplicates — but behavior varies per resource:
| Resource | Business key | Behavior on collision |
|---|---|---|
POST /v1/clients | (documentType, documentNumber, branchExternalCode) per customer | Idempotent: 200 OK with the existing client. |
POST /v1/associates | taxId per customer | Idempotent: 200 OK with the existing associate. |
POST /v1/receivable-accounts | (clientId, associateId, currencyCode) per customer | 409 receivable-account-already-exists with existingResourceId in the body. The integrator decides whether to reuse the existing one or cancel it. |
POST /v1/receivables | legalNumber per customer (when provided) | 409 duplicated-legal-number if the legalNumber already exists for that customer. If you do not send legalNumber, there is no business key and the receivable is created without a duplicate check. |
- Clients and associates: use the native idempotency. If your initial sync always posts the same data, you do not need to handle special errors — it is enough that
documentType + documentNumber(clients) ortaxId(associates) are stable in your ERP. - Receivable-accounts: explicitly handle
409 receivable-account-already-existsby recovering theexistingResourceIdfrom the response — that is the resource your ERP should keep using. - Receivables: combine a deterministic
Idempotency-Key(covers retries within 24h) with a business key unique on your side (for exampleexternalIdwith the invoice number) that lets you query before posting in bulk flows where a late retry could duplicate.