Refunds
Return funds to the customer after a successful payment. You can refund the full amount or issue multiple partial refunds.
Create a refund
Call POST /v1/payments/{id}/refunds to initiate a refund. Omit the amount field for a full refund, or include it for a partial refund.
Full refund
curl -X POST https://api.miracle.com/v1/payments/pay_abc123/refunds \
-H "Authorization: Bearer sk_test_your_secret_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: refund-order-1234" \
-d '{
"reason": "Customer requested cancellation"
}'const response = await fetch('https://api.miracle.com/v1/payments/pay_abc123/refunds', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_test_your_secret_key',
'Content-Type': 'application/json',
'Idempotency-Key': 'refund-order-1234',
},
body: JSON.stringify({
reason: 'Customer requested cancellation',
}),
});
const { data: refund } = await response.json();
// refund.status === 'pending' or 'processing'Partial refund
Pass an amount to refund less than the original payment:
curl -X POST https://api.miracle.com/v1/payments/pay_abc123/refunds \
-H "Authorization: Bearer sk_test_your_secret_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: refund-order-1234-item-a" \
-d '{
"amount": {
"currency": "USD",
"valueMinor": 2500
},
"reason": "Item returned",
"merchantReference": "return-item-a"
}'const response = await fetch('https://api.miracle.com/v1/payments/pay_abc123/refunds', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_test_your_secret_key',
'Content-Type': 'application/json',
'Idempotency-Key': 'refund-order-1234-item-a',
},
body: JSON.stringify({
amount: {
currency: 'USD',
valueMinor: 2500,
},
reason: 'Item returned',
merchantReference: 'return-item-a',
}),
});
const { data: refund } = await response.json();
// refund.amount.valueMinor === 2500Async behavior
Refunds are processed asynchronously. The API response returns the refund with an initial status (pending or processing), but the final result arrives via webhook.
- You call
POST /v1/payments/{id}/refunds— response returns theRefundobject. - The provider processes the refund (may take seconds or minutes).
- Miracle sends a
refund.succeededorrefund.failedwebhook. - Your webhook handler updates the order accordingly.
Do not retry on timeout. If your request times out, check the refund status via GET /v1/payments/{id}/refunds or wait for the webhook before attempting another refund. Duplicate refund requests with a different Idempotency-Key will create a second refund.
Refund statuses
| Status | Description | Terminal |
|---|---|---|
pending | Refund created, awaiting processing. | No |
processing | Refund submitted to the payment provider. | No |
requires_action | Provider requires payer confirmation (non-card methods only, e.g., bank transfer). | No |
succeeded | Refund completed, funds returned to customer. | Yes |
failed | Refund rejected by the provider. | Yes |
canceled | Refund canceled before completion. | Yes |
Webhooks
You receive webhook events as the refund progresses:
| Event | When |
|---|---|
refund.created | Refund has been created. |
refund.succeeded | Refund completed successfully. Funds returned to customer. |
refund.failed | Refund was rejected by the provider. |
When a refund succeeds, the parent payment status also updates:
| Event | When |
|---|---|
payment.refunded | Full refund completed. |
payment.partially_refunded | Partial refund completed, remaining balance still captured. |
Rules and limits
- Eligible payments only. You can only refund payments in
succeeded,captured, orpartially_refundedstatus. - Currency must match. The refund currency must be the same as the original payment currency.
- Multiple partial refunds allowed. You can issue several partial refunds against the same payment. The total of all refunds cannot exceed the original captured amount.
- Full refund closes the payment. After a full refund, the payment status becomes
refundedand no further refunds are possible. - Refund timing. After a successful refund, the time for funds to appear on the customer's statement depends on the payment method and issuer.
Retrieve refunds
Single refund by ID
curl https://api.miracle.com/v1/refunds/ref_xyz789 \
-H "Authorization: Bearer sk_test_your_secret_key"All refunds for a payment
curl https://api.miracle.com/v1/payments/pay_abc123/refunds \
-H "Authorization: Bearer sk_test_your_secret_key"Both endpoints return the Refund object with all fields including status, amount, reason, providerReference, and statusReason (a human-readable explanation from the provider when a refund fails).
Request fields reference
| Field | Type | Required | Description |
|---|---|---|---|
amount | MoneyAmount | No | Amount to refund. Omit for full refund. |
reason | string | No | Reason for the refund (for your records and provider). |
merchantReference | string | No | Your internal reference for this refund. |
items | LineItem[] | No | Line items being refunded (pass-through metadata). |
metadata | object | No | Key-value pairs for your own use. |
Related
- Payment Lifecycle — how refunds affect payment status
- Capture — charge an authorized payment before refunding
- Cancel / Void — release a hold without charging (before capture)
- Idempotency — safe retries for refund calls