Skip to main content
Network failures, deploy restarts, and worker crashes are normal. The right pattern for a payment API is to make every retry safe to send again. Yuno’s deduplication model is business key first, idempotency header where supported.

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 HTTP 400 instead of creating a duplicate.
ResourceField you setBehavior on duplicate
Customer (POST /v1/customers)merchant_customer_id400 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_id400 EXTERNAL_ID_EXIST. Fetch the existing recipient by merchant id.
Payout (POST /v1/payouts)merchant_referenceTreated as a unique reference. Check before retrying.
Payment Link (POST /v1/payment-links)merchant_order_idRecommended unique value. No automatic dedupe at the API.
Stable keys, safe retries
// Generate the merchant_customer_id once, persist it on your side,
// and reuse it on every retry of the same logical operation.
const merchantCustomerId = `cust-${order.userId}`;

await retryWithBackoff(() =>
  yuno.customers.create({ merchant_customer_id: merchantCustomerId })
);
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.
curl --request POST \
  --url https://api-sandbox.y.uno/v1/subscriptions \
  --header 'Content-Type: application/json' \
  --header 'public-api-key: YOUR_PUBLIC_API_KEY' \
  --header 'private-secret-key: YOUR_PRIVATE_SECRET_KEY' \
  --header 'X-Idempotency-Key: sub-9f8a3c2e-4b1d-4e7a-a8c6-1f2e3d4c5b6a' \
  --data '{ /* subscription payload */ }'
AspectBehavior
Header nameX-Idempotency-Key
RequiredNo. Optional.
ScopePer account_id and per organization.
Cache lifetimePermanent. The key is stored in a database unique index, not a TTL cache.
Replay with same keyReturns the original subscription.
Replay with same key but different bodyReturns the original subscription. The new body is not validated against the original.
Do not assume X-Idempotency-Key works on other endpoints. As of today it is only honored by POST /v1/subscriptions. On every other write endpoint, the header is ignored. Use the unique business key pattern above for those.
  1. 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.
  2. 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.
  3. For POST /v1/subscriptions, additionally send X-Idempotency-Key with a value derived from your subscription identifier.
  4. Combine with exponential backoff and jitter on 408, 429, 500, 502, 503, and 504. 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.