> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vibepay.mn/llms.txt
> Use this file to discover all available pages before exploring further.

# Refunds

> Reversing a charge in full — the only refund Vibepay supports, and why 409 means it worked.

```http theme={null}
POST /v1/transactions/{txID}/reverse
```

```bash theme={null}
curl -i -u 'term_a7f3k9d2:vpt_…' -X POST \
  https://api.vibepay.mn/v1/transactions/tx_01k2y7v9j0e8ra7cx3mbq4d5nf/reverse
```

```http 204 No Content theme={null}
```

No request body. No amount. No JSON in the response. The customer's full original amount is back
in their wallet.

## The rules

<CardGroup cols={2}>
  <Card title="Full amount only" icon="equals">
    There is no partial refund. If you need to refund part of a sale, reverse the whole charge and
    take a new one for the correct amount.
  </Card>

  <Card title="Once" icon="1">
    A second attempt returns `409`. See below — that is not a failure.
  </Card>

  <Card title="No time limit" icon="infinity">
    A charge from any date can be reversed. There is no 30-day or 180-day window.
  </Card>

  <Card title="Your charges only" icon="lock">
    Another merchant's transaction returns `403`, indistinguishable from "not a charge".
  </Card>
</CardGroup>

It also works while the customer's wallet is **suspended**. A benefit being frozen must never trap
money that is rightfully theirs.

## Treat 409 as success

```http 409 Conflict theme={null}
transaction already reversed
```

The customer has their money back. Your local record is simply behind.

<Info>
  **Converge, do not retry.** On a `409`, mark the sale refunded in your own system and show the
  cashier a success message. This is exactly what the official Vibepay POS app does, and it makes
  a refund button safe to press twice.
</Info>

```python theme={null}
r = requests.post(f"{BASE}/v1/transactions/{tx_id}/reverse", auth=AUTH, timeout=30)

if r.status_code == 204 or r.status_code == 409:
    mark_refunded(tx_id)          # both mean the money is back
elif r.status_code == 403:
    show("This transaction can't be refunded from this terminal.")
elif r.status_code == 404:
    show("Transaction not found — check the receipt number.")
else:
    show("Refund failed, please try again.")
```

## You need the transaction id

`tx_01k2y7v9j0e8ra7cx3mbq4d5nf` — the `id` from the charge response. There is no way to refund by
QR code, by amount, or by time.

<Warning>
  **Store the `id` against your own order record at the moment of sale.** If you lose it you can
  still find the transaction in `GET /v1/transactions` by amount and timestamp, but that is a
  manual, error-prone reconciliation in front of a waiting customer.
</Warning>

Printing it on the customer receipt is a good habit. It makes a walk-in refund a lookup instead of
a search.

## What happens behind the scenes

You get your `204` immediately. Three things then happen asynchronously, none of which can delay
or block the refund:

<Steps>
  <Step title="The wallet is credited">
    Already done by the time you get the `204` — this part is synchronous.
  </Step>

  <Step title="The VAT receipt is cancelled">
    Vibepay voids the receipt with the tax authority. `vatStatus` on the transaction moves to
    `voided`. You do not need to file anything.
  </Step>

  <Step title="Settlement is adjusted">
    If the original charge had already been paid out to you in an earlier weekly cycle, the amount
    is netted off your next payout. Nothing is clawed back from your bank account.
  </Step>
</Steps>

## Errors

| Status | Body                           | Meaning                                         |
| ------ | ------------------------------ | ----------------------------------------------- |
| `204`  | *(empty)*                      | Reversed                                        |
| `400`  | `invalid transaction id`       | The id is malformed — check the `tx_` prefix    |
| `401`  | `unauthorized`                 | Credentials dead                                |
| `403`  | `forbidden`                    | Another merchant's transaction, or not a charge |
| `404`  | `transaction not found`        | No such id                                      |
| `409`  | `transaction already reversed` | **Already refunded — treat as success**         |
| `5xx`  | `internal error`               | Retry; the operation is safe to repeat          |

Reversal is naturally idempotent — the worst a repeat can do is return `409` — so it takes no
`Idempotency-Key` and none is needed.

<Card title="What happens to the tax receipt" icon="receipt" href="/vat-receipts" horizontal>
  How VAT issuance and voiding work, and how to observe them.
</Card>
