Idempotency
Use the Idempotency-Key header to safely retry requests without creating duplicate operations.
Why idempotency matters
Network errors, timeouts, and client-side retries can leave you unsure whether a request succeeded. Without idempotency, retrying a failed request could create duplicate payments, double-charge a customer, or issue a refund twice. The Idempotency-Key header ensures each operation happens exactly once — even if you send the same request multiple times.
How to use
Send an Idempotency-Key header on every write request (POST, PUT, PATCH, DELETE). The value should be a UUID — this is the recommended format. Any unique string is accepted, but UUID is preferred.
If you retry a request with the same key, Miracle returns the original response without executing the operation again.
curl -X POST https://api.miracle.com/v1/payments \
-H "Authorization: Bearer sk_test_your_secret_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order_12345_payment" \
-d '{
"amount": { "currency": "USD", "valueMinor": 5000 },
"merchantReference": "order-12345",
"paymentMethod": { "type": "card", "token": "tok_xxx" }
}'const response = await fetch('https://api.miracle.com/v1/payments', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_test_your_secret_key',
'Content-Type': 'application/json',
'Idempotency-Key': 'order_12345_payment',
},
body: JSON.stringify({
amount: { currency: 'USD', valueMinor: 5000 },
merchantReference: 'order-12345',
paymentMethod: { type: 'card', token: 'tok_xxx' },
}),
});
const { data: payment } = await response.json();Behavior
| Scenario | Result |
|---|---|
| Same key + same request body | Returns the cached original response. No new operation is created. |
| Same key + different request body | Returns 409 Conflict with error code DUPLICATE_TRANSACTION. |
| Key not provided on a write endpoint | Returns 400 Bad Request. |
Keys expire after 24 hours by default. The TTL is configurable at the platform level with a minimum of 24 hours. After expiration, the same key can be reused for a new request.
Keys are scoped per merchant and per endpoint — the same key used by different merchants or on different endpoints will not conflict.
When to use
The Idempotency-Key header is required on all write operations. The most important endpoints:
| Endpoint | Operation |
|---|---|
POST /v1/payments | Create a payment |
POST /v1/payments/{id}/capture | Capture an authorized payment |
POST /v1/payments/{id}/refund | Refund a payment |
POST /v1/payments/{id}/cancel | Cancel / void a payment |
POST /v1/checkout-sessions | Create a checkout session |
POST /v1/hpp/sessions | Create a hosted payment page session |
Best practices
- Always send an idempotency key on write operations. Requests without a key are rejected.
- Use deterministic keys tied to your business logic rather than random UUIDs. For example,
{orderId}_paymentfor payment creation and{orderId}_refundfor refunds. This way, retries happen naturally even if your client crashes and restarts. - Don't reuse keys across different operations. A key used for payment creation should not be reused for a refund — even after the 24-hour expiration.
- Store the key alongside your order record so you can retry with the same value if needed.
Related
- Payment Lifecycle — payment statuses and transitions
- Capture — capture with idempotency
- Refunds — refund with idempotency
- Cancel / Void — cancel with idempotency