Reliability
Idempotency, retries, webhook delivery and reconciliation — how not to charge twice and how not to miss a payment.
Idempotency
Send Idempotency-Key on every call that moves money. A repeat with the same key and the same body returns the original response with Idempotent-Replayed: true — no second charge. The same key with a different body is a bug on your side and we say so: 409 idempotency_conflict.
order-88213-attempt-1 is stable across a timeout, a process restart and a token refresh.The scope is your merchant, so a retry keeps deduplicating even if your bearer token rotated in between. Records live for 24 hours.
Rate limits
Limits are per merchant and follow your tier — your traffic is never affected by another merchant's. Rejections return 429 with Retry-After and the X-RateLimit-* headers. Back off on the header; do not retry immediately.
Webhook delivery
19Exponential (2^n minutes), capped at 6 hours~74 hours10 secondsReturn 2xx as soon as you have persisted the event and do the real work asynchronously. Anything slower than 10 seconds is recorded as a failure and retried, which is how an endpoint that is merely slow ends up processing the same event repeatedly.
After the window an event is parked rather than dropped: when your endpoint starts responding again we drain what accumulated. You will not silently lose the events from an outage.
X-Key2Pay-Delivery arrives twice. Deduplicate on it, or on the transaction id plus the event name.Reconciliation
Poll GET /payments/{id} for anything still pending after its due date. It is authoritative — we query the provider behind it — and it is what closes the gap when a webhook is lost.
/api/v1/payments/{id}secret keyAuthoritative status of one charge. Use it as your reconciliation backstop.
curl https://sandbox.key2pay.ai/api/v1/payments/TXN-MT8E00EB-V5OK \ -H "Authorization: Bearer sk_test_…"
{
"id": "TXN-MT8E00EB-V5OK",
"status": "completed",
"amount": 100,
"currency": "USD",
"amountLocal": 1820.50,
"currencyLocal": "MXN",
"merchantOrderId": "order-88213",
"timestamps": { "created": "…", "paymentReceived": "…" }
}