Use unique business keys
Every write endpoint that creates a top level resource accepts an external identifier you control. Pick a stable, unique value for each logical operation. If a retry sends the same value, the database constraint rejects it with HTTP400 instead of creating a duplicate.
| Resource | Field you set | Behavior on duplicate |
|---|---|---|
Customer (POST /v1/customers) | merchant_customer_id | 400 CUSTOMER_ID_DUPLICATED. Look up the existing record with Retrieve Customer by External ID. |
Payment (POST /v1/payments) | merchant_order_id (on the checkout session, propagated to the payment) | Look up the existing payment with Get Payment by Merchant Order before retrying. The orchestrator does not auto reject duplicate POST /v1/payments calls, so your retry strategy must check first. |
Recipient (POST /v1/recipients) | merchant_recipient_id | 400 EXTERNAL_ID_EXIST. Fetch the existing recipient by merchant id. |
Payout (POST /v1/payouts) | merchant_reference | Treated as a unique reference. Check before retrying. |
Payment Link (POST /v1/payment-links) | merchant_order_id | Recommended unique value. No automatic dedupe at the API. |
Stable keys, safe retries
Two simultaneous requests with the same business key can race. The first one wins. The loser receives the duplicate error. Treat that error as success and refetch the existing record by the same merchant id.
X-Idempotency-Key for subscriptions
POST /v1/subscriptions is the one endpoint that accepts the X-Idempotency-Key header today. The dedupe scope is per account and per organization, and it is permanent (a database unique index). On replay, the original Subscription response is returned without re creating the record.
| Aspect | Behavior |
|---|---|
| Header name | X-Idempotency-Key |
| Required | No. Optional. |
| Scope | Per account_id and per organization. |
| Cache lifetime | Permanent. The key is stored in a database unique index, not a TTL cache. |
| Replay with same key | Returns the original subscription. |
| Replay with same key but different body | Returns the original subscription. The new body is not validated against the original. |
Recommended client pattern
- Generate a stable business key (
merchant_customer_id,merchant_order_id,merchant_recipient_id) for every logical write operation. Persist it in your database before sending the request. - On retry, send the exact same key. If the server rejects with a duplicate error, look up the resource by that merchant id and treat the result as success.
- For
POST /v1/subscriptions, additionally sendX-Idempotency-Keywith a value derived from your subscription identifier. - Combine with exponential backoff and jitter on
408,429,500,502,503, and504. See Error handling.
What next
Error handling
The error envelope and which codes are safe to retry.
Authentication
The three required headers and key rotation.
Get Customer by External ID
Look up a customer by your
merchant_customer_id.Get Payment by Merchant Order
Look up a payment by your
merchant_order_id.