# Refunds (/en/features/transactions/refunds)

Issue a refund from the dashboard, read refund state across the dashboard, and understand fees and timing.

A <ApiLink href="/api-reference/objects/refund">Refund</ApiLink> returns money you captured from a customer back to their original payment method. The dashboard does not have a separate "Refunds" page. Instead, every refund lives on the payment it came from, and the running record of every refund is in the **All activity** tab on Transactions.

This page covers how to issue a refund from the payment detail page, when the dashboard hides the refund button, what the customer experiences, and where the refund shows up across the rest of the dashboard.

## What you can do [#what-you-can-do]

<Cards>
  <Card icon="<RotateCcw />" title="Refund any captured payment">
    A **Refund** button sits on the detail page of any successful payment. Issue a full refund or,
    when the processor supports it, a partial one.
  </Card>

  <Card icon="<Tags />" title="Tag a reason">
    Optionally label the refund as **Requested by customer**, **Duplicate**, or **Fraudulent** so
    you can correlate later.
  </Card>

  <Card icon="<ListOrdered />" title="Track every refund">
    Refunds show up in the payment's timeline, on the payment's status, in the Payment Breakdown
    math, and as their own row in **All activity**.
  </Card>

  <Card icon="<Wallet />" title="See the impact on your balance">
    Each refund is a balance entry that reduces your available balance. Customer lifetime spend is
    also adjusted automatically.
  </Card>
</Cards>

## Issue a refund [#issue-a-refund]

<Steps>
  <Step>
    ### Open the payment [#open-the-payment]

    In **Transactions**, find the payment you want to refund and click it. The detail page opens.
  </Step>

  <Step>
    ### Click Refund [#click-refund]

    In the top right of the header, click **Refund**. A side sheet opens, titled **Refund payment**.

    If you don't see a **Refund** button, see [When the button is hidden or disabled](#when-the-button-is-hidden-or-disabled) below.
  </Step>

  <Step>
    ### Set the amount [#set-the-amount]

    The amount field is prefilled with the **Available to refund** value at the bottom of the sheet (the original captured amount minus anything you've already refunded).

    * For a full refund, leave the amount as is.
    * For a partial refund, lower the value. Partial amounts must be greater than zero and at most the available-to-refund value.

    If the payment processor doesn't support partial refunds, the field is locked at the full amount and the sheet shows "Only full refunds are supported for this payment processor".
  </Step>

  <Step>
    ### (Optional) Pick a reason [#optional-pick-a-reason]

    The **Reason** select offers three values: **Requested by customer**, **Duplicate charge**, **Fraudulent**. Leave it blank if none apply. Reasons help when you filter or report on refunds later, but they don't change how the refund is processed.
  </Step>

  <Step>
    ### Submit [#submit]

    Click the **Refund** button at the bottom of the sheet (the label shows the exact amount you're about to refund). On success the sheet closes and a "Refund processed successfully" toast confirms. The payment's status flips to **Partially Refunded** or **Refunded** and a refund entry appears at the top of the timeline.
  </Step>
</Steps>

## When the button is hidden or disabled [#when-the-button-is-hidden-or-disabled]

The dashboard hides or disables the **Refund** button when a refund can't go through.

**Hidden** when the payment isn't in a refundable state:

| Reason                                                 | Why                                                                                                                    |
| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| Status is not **Successful** or **Partially Refunded** | Failed, canceled, expired, and pending payments don't have captured funds to refund.                                   |
| The payment is not yet captured                        | Authorized but not captured: there's no charge to reverse yet.                                                         |
| The payment is already fully refunded                  | Nothing left to refund.                                                                                                |
| The payment method doesn't support refunds             | Some processors and methods route refunds out of band. The button is hidden when XPay can't issue the refund directly. |

**Disabled with a tooltip** when you have permission gaps or a hold:

| Tooltip                                                 | Why                                                                                |
| ------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| "You don't have permission to perform this action"      | Your role lacks refund-write permission. Ask an account admin to grant it.         |
| "This charge is currently under an active risk hold..." | A risk hold is on the funds. Contact support to release the hold before refunding. |

If you submit and hit a server-side block, the dashboard surfaces a specific error toast. Common ones:

* "This charge cannot be refunded in its current state."
* "Refund amount is invalid or exceeds the refundable amount."
* "Insufficient merchant balance to process this refund."

## What happens after [#what-happens-after]

**Timing.** Refunds take 7 to 14 days to land on the customer's statement. The dashboard reflects the refund immediately; the customer's bank takes longer.

**Fees.** XPay's fees on the original payment are not returned with the refund, but XPay does not charge an additional fee on top of the refund itself.

**Destination.** Refunds always go back to the original payment method. There is no way to redirect a refund to a different card or account.

**Customer record.** The customer's lifetime spend on their profile updates: **Total Refunded** goes up, **Net Spend** goes down. **Total Spent** (gross) stays as the historical record of what they've ever been charged.

## Where refund state shows up [#where-refund-state-shows-up]

Across the dashboard, a refund is visible in seven places:

| Surface                                              | What it shows                                                                                                |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| **Payments tab → Status column**                     | The badge on the payment row flips to **Refunded** or **Partially Refunded**. Hover for the refunded amount. |
| **Payments tab → Refunded Date column**              | When the column is on, this shows when the most recent refund was issued.                                    |
| **Payment detail → Header status badge**             | Same status badge on the detail page.                                                                        |
| **Payment detail → Timeline**                        | One entry per refund (succeeded, pending, or failed) with the amount and reason.                             |
| **Payment detail → Payment Breakdown**               | A **Refunded amount** line appears in the math, reducing **Net amount**.                                     |
| **Transactions → All activity tab**                  | The refund is its own balance entry, alongside its parent charge, fees, and any adjustments.                 |
| **Customer profile → Insights and Customer Details** | **Total Refunded** updates; **Net Spend** is recalculated.                                                   |

If you also subscribe to webhooks, the refund fires `refund.created`, and `refund.failed` if the bank rejects it. See [Refunds](/integrate/refunds) for the developer-side companion (status fields, the Refund object, and webhook handling).

## Refund reasons [#refund-reasons]

The dashboard offers three reason codes when you issue a refund. They're optional and free-form text isn't supported.

| Reason                    | When to pick it                                                                                                      |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **Requested by customer** | The customer asked for the refund (returned a product, changed their mind, billing dispute resolved in their favor). |
| **Duplicate charge**      | The same payment went through twice and you're reversing the duplicate.                                              |
| **Fraudulent**            | You believe the original payment was unauthorized or fraudulent.                                                     |

Reasons are surfaced on the timeline entry and on the Refund object in API responses and webhook payloads.

## Where to next [#where-to-next]

<Cards>
  <Card icon="<Receipt />" title="Payments" href="/features/transactions/payments">
    The list and detail page where the **Refund** button lives.
  </Card>

  <Card icon="<ArrowLeftRight />" title="Balance and activity" href="/features/transactions/balance-and-activity">
    Where every refund shows up as its own row in the running ledger.
  </Card>

  <Card icon="<Hourglass />" title="Refunds (developer)" href="/integrate/refunds">
    The code-side companion. The Refund object, status fields, and webhook events.
  </Card>

  <Card icon="<Ban />" title="Disputes (out of band)">
    Disputes are not yet self-serve in the dashboard. If a customer disputes a charge with their
    bank, XPay reaches out directly.
  </Card>
</Cards>