# Undo an offline refund

> Reverses a refund record out of every total it touched, leaving the refund itself DECLINED and the payment refundable again. It is meant for an offline refund (

Reverses a refund record out of every total it touched, leaving the refund itself DECLINED and the payment refundable again. It is meant for an offline refund (`manual` true, `gateway_status` MANUAL) — a record of a refund made by some other means, where no money ever left through a gateway, so the record can simply be withdrawn. Nothing is sent to the gateway, in either direction: this changes Shuttle's books only. The refund rules themselves, including what may be refunded and how an offline refund is recorded, belong to [Refund a payment](/platforms/user-guides/rules-payment-refund/).

### Where you can do this

- Merchant View: [Transaction](/platforms/user-guides/admin-transactions-view/) (Undo Refund)

### Before you start

- The object is a refund (a `ref_` id; the same refund read as a transaction carries a `tr_` id, and Merchant View swaps the prefix before calling).
- Its status is SUCCESS, PENDING, DECLINED, UNRESOLVED or REQAUTH, or its `gateway_status` is MANUAL, which is accepted whatever the status. A refund left in ROLLBACK or UNATTRIBUTED cannot be undone.
- The payment or capture the refund was taken from, and the refund's contract, still exist.
- No other payment, refund or capture is being processed on that contract at the same moment.
- Merchant View only offers the control on an offline refund that is not already DECLINED; the server is broader (see Rules).

### 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 |
|---|---|---|
| `refund_id` must resolve to a transaction: the `ref_` prefix is stripped and the object fetched directly; anything else is searched as an `alt_key` among payment transactions, newest first | The API | NOT_FOUND: "Transaction not found" (404) |
| The object must be a refund (`action` REFUND) | The API | VALIDATION_ERROR: "Not a refund" (400) |
| Shuttle re-checks that the id names an existing transaction and that it is a refund | The API | INVALID (BOLT-7048) |
| The refund's status must be SUCCESS, PENDING, DECLINED, UNRESOLVED or REQAUTH, or its `gateway_status` must be MANUAL; a refund in ROLLBACK or UNATTRIBUTED is refused | The API | INVALID (BOLT-7048) |
| The contract must not be busy: the reversal takes the contract's processing lock, waiting up to 30 seconds for it | The API | INVALID (BOLT-7059) with Shuttle's lock message |

### What happens

- The refund becomes DECLINED. Its `amount` and `reason` are left as recorded, `processed` is filled in if it was empty, and the caller receives the refund object back with HTTP 200 as `{ refund }`, honouring `expand`.
- Everything the refund took away is put back: the payment's `balance` rises and its `refunded` falls by the refund amount, and the charge, instalment and contract completed and refunded totals follow. A refund of a capture also restores the authorisation the capture came from. A payment taken in a terminal batch has its batch item amount restored.
- The payment is refundable again for the amount put back, through [Refund a payment](/platforms/user-guides/rules-payment-refund/) as usual.
- No money moves and nothing is sent to the gateway. For an offline refund, which was only ever a record, that is the whole story; a gateway refund that really settled is not pulled back, only the books change.
- Webhooks: REFUND.FAILED for the refund, PAYMENT.UPDATE for the payment it was taken from (CAPTURE.UPDATE plus the authorisation's PAYMENT.UPDATE when the refund was against a capture), and CONTRACT.UPDATE unless the contract is a one-off due at once. No email or SMS is sent to the payer.
- The contract's last transaction becomes this declined refund, so the contract's latest activity reads as a declined refund from then on.

### Who can do this

- Roles: Admin and Support.
- Permission keys (for clients managing permissions directly): any one of `agreement`, `payment_contract`, `payment_transaction` or `*`, held on the client or on the refund's division; Admin's `*` satisfies it. Without one the caller sees UNAUTHORIZED: "Unauthorized" (401). A session tied to an account is accepted for that account's own refunds without holding any of these keys; a session tied to a different account is refused with NO_PERMISSION: "No permissions" (403). Merchant View shows the control to any user holding `payment_transaction` (or `*`) on any object, so a user whose key is on neither the client nor the refund's division sees the control and is refused by the server, while a user holding only `agreement` or `payment_contract` on the division is accepted by the server but never sees the control.
- App options: none. Unlike Refund, the Undo Refund control is not behind the instance's `refund` option; it appears whenever the refund qualifies and the user holds the key.
- API keys: an instance session reaches the route with the same check. The route is not part of the public API spec (see Rules).

### Related

- [Refund a payment](/platforms/user-guides/rules-payment-refund/)
- [Mark an unresolved payment approved or declined](/platforms/user-guides/rules-payment-override-status/)