Refunds
Create, limit, and track API refunds against captured payment amounts
A refund can return only money that has already been captured. If a payment is still authorized and waiting for capture, capture it before requesting a refund. For a partially captured payment, the captured amount is the upper bound.
Use POST /refunds to create a refund. Use GET /refunds/{refund_id} to retrieve it and track its API status.
Create a refund
The request schema has 10 fields. These 6 cover the common full and partial refund flow:
payment_id
Yes
The payment to refund.
amount
No
The amount in the currency's lowest denomination. Send it for a partial refund.
refund_id
No
Your unique identifier for this refund operation. Hyperswitch generates one when it is omitted.
reason
No
A reason string. Hyperswitch stores it on the refund; whether it is passed to the processor depends on the connector.
metadata
No
Additional structured information for your own reference.
refund_type
No
instant (the default) sends the refund to the connector before responding. scheduled queues it instead. This decides the status in the create response, as described below.
For a partial refund:
{
"payment_id": "<payment_id>",
"refund_id": "<refund_id>",
"amount": 500,
"reason": "Customer returned the item",
"metadata": {
"order_reference": "test_order"
}
}The response schema has 21 fields. The 7 used in this flow are refund_id, payment_id, amount, currency, status, reason, and metadata.
Full and partial refund amounts
Omit amount only when you intend to refund the complete captured amount and no earlier non-failed refund has consumed part of it. Hyperswitch initially substitutes amount_captured when amount is omitted, then applies the remaining-amount check.
For each refund, Hyperswitch calculates:
remaining refundable amount = amount_captured - previous non-failed refund amounts
The new amount must not exceed that remainder. Failed refund operations do not consume it. This allows multiple partial refunds, subject to the remaining captured amount and the deployment's refund.max_attempts limit. Hyperswitch rejects a new refund when the payment attempt already has more than refund.max_attempts refunds, so the default of 10 allows up to 11 refunds on one payment attempt. That off-by-one is a bug, not an intended limit; see the tracking issue. Every refund counts toward that limit, including failed ones, even though failed refunds do not consume the refundable amount. Deployments can configure a different value. Use a distinct refund_id for each operation.
Refunds and captures
A payment is eligible for this refund flow when its payment status is succeeded or partially_captured. A payment waiting for capture is not eligible.
The limit is based on amount_captured, not the originally requested payment amount. If you captured only part of an authorization, you can refund only that captured portion.
A payment that had a void issued after capture cannot be refunded; the request fails with a precondition error.
The connector must also support refunds for the payment's method type. If the connector does not declare refund support for that payment method type, Hyperswitch rejects the request before creating a refund, with the message Refunds are currently not supported for <payment method type> transactions via <connector>. Call GET /feature_matrix to see which payment method types each connector declares refund support for.
Refund status lifecycle
The API returns four wire values:
pending
The refund was created and does not yet have a final processor outcome.
succeeded
The refund completed.
failed
The refund did not complete.
review
The refund requires review.
Hyperswitch records every new refund as pending, but that is not necessarily the status you get back from POST /refunds. With the default refund_type of instant, Hyperswitch sends the refund to the connector before responding, so the create response can already be succeeded, failed, or review, and is pending only when the connector has not reached a final outcome. With refund_type: scheduled, the refund is queued and the create response is pending.
Retrieve a refund with GET /refunds/{refund_id} when you need the current API value. For event-driven updates, use the refund events listed in Webhooks.
The Control Center uses display labels for operators. See Operations for the dashboard workflow. Those labels and the API wire values serve different interfaces.
Refund windows
Hyperswitch checks the time since the payment was created (not since it was captured) against the deployment's refund.max_age setting, in days, before creating the refund. The repository default is 365 days, but deployments can configure a different value.
This server-side eligibility window is separate from the time it takes a refund to appear on a customer statement. Statement timing is owned by the payment processor and issuer, and the Hyperswitch API does not define a fixed duration.
See the Create a Refund and Retrieve a Refund API references for the complete schemas.
Last updated
Was this helpful?

