# User guides: Refund a payment

> Returns money to the payer for a successful payment or capture, in full or in part, either through the gateway that took the payment or as an offline record of

Returns money to the payer for a successful payment or capture, in full or in part, either through the gateway that took the payment or as an offline record of a refund made by other means (`manual` true). A refund is its own transaction (a `ref_` id) attached to the source; it reduces the source's `balance` and the completed totals of the charge, instalment and contract the source paid. Note the route: `POST /c/api/payments/:id/refund`, singular. An authorisation is not refunded; it is voided ([Void an authorisation](/platforms/user-guides/rules-payment-void/)) or captured first ([Capture an authorised payment](/platforms/user-guides/rules-payment-capture/)).

### Where you can do this

- Merchant View: [Refund a transaction](/platforms/user-guides/admin-transaction-refund/) (Refund)
- Merchant View: Refund
- API: [Refund a payment](/platforms/reference/operations/refundpayment/) (`POST /payments/{payment_id}/refund`) — amount, reason, manual true for an offline refund; :id is a payment, capture or transaction id, or the payment's alt_key
- API: [Refund a capture](/platforms/reference/operations/refundcapture/) (`POST /captures/{capture_id}/refund`) — the same body against a capture id; same server rules, without Shuttle's lookup and count checks

### Before you start

- The source is a payment or a capture. Authorisations, voids and refunds cannot be refunded.
- The source is in status SUCCESS, PENDING or UNATTRIBUTED and has a `balance` above zero (the part not yet refunded; for an UNATTRIBUTED payment, its full amount).
- The source has been processed (has a `processed` date) unless it is UNATTRIBUTED, and its contract and the allocations that record which charges it paid still exist.
- The gateway the source was taken on is active. An archived or inactive gateway blocks the refund even when it is offline.
- For a gateway refund (`manual` absent or false): the gateway accepts a refund of this kind for the payment method type, a full refund when the amount equals the original amount, a partial refund otherwise. The gateway advertises this as the features FULL_REFUND, PARTIAL_REFUND and MULTI_REFUND under its `payment_method_types`.
- From Merchant View: the instance option `refund` is on; `refund_offline` is on for the Offline choice to be offered.

### Inputs

| Input | Required | Meaning and constraints |
|---|---|---|
| amount | no | Decimal as a string (`"10.00"`), above zero and no more than the source's `balance`. Omitted: the whole remaining balance. Any number of decimals is accepted server-side; Merchant View allows at most two and at least one minor unit. |
| reason | no | Free text. Omitted: recorded as "No reason given". The public spec marks it required; Merchant View limits it to 100 characters; the server does not limit its length. |
| manual | no | `true` records an offline refund: no gateway call, no money moves, no fee, `gateway_status` MANUAL. Default false. |

### Rules

*Checked by* **the API** means Shuttle refuses the request however it is sent; a screen name means only that screen refuses it, and a direct API call would be accepted.

| Rule | Checked by | What you see |
|---|---|---|
| `:id` on the payments route must resolve to a PAYMENT or CAPTURE transaction; a prefixed id is looked up directly, anything else is searched as the payment's `alt_key`, ignoring DECLINED, INVALID_PAYMENT_METHOD and REQAUTH attempts (newest match wins) | The API | NOT_FOUND: "Payment not found" |
| An `alt_key` that matches two payments that were not DECLINED, INVALID_PAYMENT_METHOD or REQAUTH, the older created within the last six months, is ambiguous | The API | INVALID_REQUEST: "Duplicate alt_key" |
| The source must be SUCCESS, PENDING or UNATTRIBUTED | The API | VALIDATION_ERROR (BOLT-9005) |
| `amount` must be above zero; zero, negative or a source with nothing left to refund | The API | INVALID_AMOUNT (BOLT-1168) |
| `amount` must not exceed the remaining `balance` | The API | VALIDATION_ERROR (BOLT-9003) |
| A source already fully refunded is reported as such, with a distinct message when the earlier refund was offline | The API | VALIDATION_ERROR (BOLT-1148: "already refunded" / "already refunded manually") |
| The source must have a `processed` date (unless UNATTRIBUTED), an existing contract, and intact charge allocations | The API | INVALID_AMOUNT (BOLT-1168) when unprocessed; VALIDATION_ERROR (BOLT-1148) when the contract or an allocation is missing |
| The source's gateway must be active, also for an offline refund | The API | INVALID (BOLT-7185, archived or inactive gateway) |
| The source must have a payment connector | The API | VALIDATION_ERROR (BOLT-9008) |
| A gateway refund needs the gateway to accept the refund type for the payment method type: TOTAL_AMOUNT when `amount` equals the source's original amount, PARTIAL_AMOUNT otherwise. The type is judged against the original amount, not the balance, so a second refund that clears the balance is sent as partial; a gateway that only accepts full refunds rejects it | The API | VALIDATION_ERROR (BOLT-9006) |
| A gateway that fails to answer leaves the refund UNRESOLVED and fails the call | The API | INVALID (BOLT-1147) |
| A gateway decline is not an error response: the call returns the refund with status DECLINED and the gateway's `gateway_message`; UNRESOLVED likewise. Only a request that produced no refund record fails | The API | HTTP 200 with `refund.status` DECLINED or UNRESOLVED |
| Any refund is allowed while the gateway's refund types allow it; the server has no notion of "one refund only". The gateway's MULTI_REFUND feature is advisory and is applied by Merchant View only | The API | none |
| Merchant View locks the whole amount to `balance`; a partial amount is editable only when the gateway advertises PARTIAL_REFUND, the whole-amount toggle only with FULL_REFUND; an offline refund allows both | Merchant View screen | inputs disabled |
| Merchant View sends `manual` true when Offline is chosen, when the source is itself an offline (MANUAL) payment, or when the gateway lacks MULTI_REFUND and the amount differs from the balance; it forces Offline when the gateway advertises neither FULL_REFUND nor PARTIAL_REFUND; it warns that a partial refund on a gateway without MULTI_REFUND leaves the remainder unrefundable via the gateway | Merchant View screen | "This will record the refund only..." warnings; confirmation modal |
| Merchant View rejects a partial amount below one minor unit, above `balance`, or with more than two decimals; the server accepts any scale | Merchant View screen | Refund button disabled |

### What happens

- The caller receives a refund object (`ref_` id, `amount`, `reason`, `reference`, `status`, `gateway_status`, `gateway_message`, `processed`, `source_transaction`, `payment`) with status SUCCESS, PENDING, DECLINED or UNRESOLVED. A decline still returns HTTP 200; check `status`. Only a request that could not create a refund at all returns an error.
- On SUCCESS or PENDING the source's `balance` falls and `refunded` rises by the amount; the charge, instalment and contract completed totals fall with it and the contract's last transaction becomes the refund. Refunding a capture also reduces the parent authorisation's balance. A payment taken in a terminal batch has its batch item amount reduced to what remains.
- An offline refund (`manual` true) moves no money: it is recorded SUCCESS at once with `gateway_status` MANUAL and no fee, and can be reversed with [Undo an offline refund](/platforms/user-guides/rules-payment-undo-refund/). A gateway refund returns the money through the gateway, records the gateway's fee (from its response or its refund settings) and cannot be reversed here.
- Webhooks: REFUND.SUCCESS, REFUND.PENDING, REFUND.FAILED or REFUND.UNRESOLVED for the refund; PAYMENT.UPDATE for the refunded payment (CAPTURE.UPDATE for a refunded capture, plus PAYMENT.UPDATE for its authorisation); CONTRACT.UPDATE on success or pending unless the contract is a one-off due at once or still being set up. No email or SMS is sent to the payer.
- Later: a PENDING refund is completed when the gateway confirms it, at which point the totals move and REFUND.SUCCESS arrives. An UNATTRIBUTED source becomes SUCCESS as a side effect of being refunded.

### Who can do this

- Roles: Admin and Support.
- Permission keys (for clients managing permissions directly): `payment_transaction.refund` on any one of the source's branding, channel, designation, initiative, legal entity, gateway or receipt template; Admin's `*` satisfies it. A plain `payment_transaction` grant does not satisfy it: Shuttle matches the access level exactly, so the grant must name `payment_transaction.refund` or `*`. Without it the caller sees INVALID (HTTP 400) carrying Shuttle's permission message rather than NO_PERMISSION. Merchant View requires `payment_transaction` to open the screen and `payment_transaction.refund` on the source's initiative (or `*`) to show the form, otherwise "You do not have permissions to refund this transaction.".
- Admin app options: `refund` shows the Refund control; `refund_offline` offers the Offline choice (Offline is forced regardless when the gateway advertises neither FULL_REFUND nor PARTIAL_REFUND).
- API keys: an instance session reaches the route with the same server permission check; no additional option applies.

### Related

- [Undo an offline refund](/platforms/user-guides/rules-payment-undo-refund/)
- [Capture an authorised payment](/platforms/user-guides/rules-payment-capture/)
- [Void an authorisation](/platforms/user-guides/rules-payment-void/)
- [Take a one-off payment](/platforms/user-guides/rules-payment-take/)