# Refund, reverse, or cancel a payment

> Choose between cancel, refund, and reversal for card and ACH payments, call each endpoint, issue partial refunds, and watch the webhooks.

> AI agents: search and read all Moov docs with the docs MCP server at https://docs.moov.io/mcp (no API key). Setup: https://docs.moov.io/guides/developer-tools/mcp-ai/docs-mcp/index.md

Give a customer their money back the right way for the rail and the moment: cancel before it settles, refund after, and let reversals pick for you on cards.

Moov has three ways to undo a payment, and which one works depends on the payment rail and how far the transfer has progressed. In this recipe, you'll look up the transfer, choose the right action, call the endpoint, and track the result. All three endpoints are called on your **partner account** (the account that created the transfer) and need the `/accounts/{accountID}/transfers.write` scope if you use an access token.

## Prerequisites

- Everything in [Before you start](/guides/get-started/recipes/index.md#before-you-start): test mode API keys, authentication headers, your partner account ID, and a webhook endpoint
- Token scopes: `/accounts/{partnerAccountID}/transfers.read` and `/accounts/{partnerAccountID}/transfers.write`
- A completed or pending test transfer to undo. The [accept a card payment recipe](/guides/get-started/recipes/accept-card-payment/index.md) creates a card payment; the [wallet funding recipe](/guides/get-started/recipes/wallet-funding/index.md) creates an ACH debit.
- Webhooks subscribed to `transfer.updated`, `refund.created`, `refund.updated`, `cancellation.created`, and `cancellation.updated`. See [handle webhooks end to end](/guides/get-started/recipes/webhooks/index.md).

## Choose the right action

| Payment | Where it is                           | Action                                                                                  | Endpoint                                                                                                     |
|---------|---------------------------------------|-----------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|
| Card    | Authorized, not yet settled           | **Cancel**: reverses the authorization, so the charge drops off and no refund is needed | `POST .../reversals` (recommended) or `POST .../cancellations`                                               |
| Card    | Settling or settled                   | **Refund**: sends a credit back to the card                                             | `POST .../reversals` (recommended) or `POST .../refunds`                                                     |
| Card    | Any, for part of the amount           | **Partial refund**: partial cancellations aren't supported                              | `POST .../reversals` with an `amount`, or `POST .../refunds` with an `amount` if the payment had a surcharge |
| ACH     | Before the debit or credit originates | **Cancel**                                                                              | `POST .../cancellations`                                                                                     |
| ACH     | After it originates                   | No cancel or refund endpoint; refunds are for card transfers only                       | Send the money back with a new transfer                                                                      |

A **reversal** is the simplest choice for cards. Moov checks the settlement status for you, then cancels the transfer if it can and refunds it if it can't. Cancelling is cheaper and returns funds to the cardholder faster than a refund. Don't cancel, refund, or reverse a card payment the cardholder has disputed; that risks paying them twice. Deeper guide: [reversals](/guides/accept-payments/cards/reversals/index.md).

## Steps

### Look up the transfer

Get the transfer with the get transfer [endpoint](/api/money-movement/transfers/get/index.md) to see its rail and state:

```zsh
curl -X GET "https://api.moov.io/accounts/{partnerAccountID}/transfers/{transferID}" \
  -H "Authorization: Bearer {token}" \
  -H "X-Moov-Version: v2026.07.00"
```

Check these fields:

- `source.paymentMethodType`: `card-payment` for cards; `ach-debit-fund` or `ach-debit-collect` for ACH debits
- `source.cardDetails.status`: `initiated` or `confirmed` means the card payment hasn't settled, so it can still be canceled. `settled` or `completed` means it needs a refund.
- `source.achDetails.status` and `destination.achDetails.status`: `initiated` means the ACH leg hasn't originated yet, so you can cancel; `originated` or later means you can't
- `status`: `failed` or `canceled` transfers have nothing to undo, and `reversed` means the funds already went back
- `refundedAmount`, `refunds`, and `cancellations`: any earlier attempts on this transfer

Deeper guides: [card payment timing](/guides/accept-payments/cards/timing/index.md) and [ACH statuses](/guides/money-movement/ach/ach-details/index.md#ach-statuses).

### Reverse a card payment

For a full refund of a card payment, call the reversals [endpoint](/api/money-movement/refunds/cancel/index.md) without a body. An `X-Idempotency-Key` is required; reuse it if you retry the same request.

```zsh
curl -X POST "https://api.moov.io/accounts/{partnerAccountID}/transfers/{transferID}/reversals" \
  -H "Authorization: Bearer {token}" \
  -H "X-Moov-Version: v2026.07.00" \
  -H "X-Idempotency-Key: 3b9c2f1e-8a47-4d6b-9e21-5c7f0a1d2e34"
```

The response tells you which action Moov took. A `cancellation` object, usually with `status` `completed`, means the authorization was reversed and the transfer's `status` becomes `canceled`. A `refund` object, usually `pending` at first, means a credit is on its way to the card. You can also start a reversal in the Dashboard from the transfer's details page with the **Refund** button. Deeper guide: [reversals](/guides/accept-payments/cards/reversals/index.md).

### Refund part of a card payment

To return part of the amount, send an `amount` in cents. Because a card authorization can't be partially canceled, Moov always issues a refund for a partial amount, even if the payment hasn't settled. The cardholder sees the full charge, then a credit for the partial amount.

```zsh
curl -X POST "https://api.moov.io/accounts/{partnerAccountID}/transfers/{transferID}/reversals" \
  -H "Authorization: Bearer {token}" \
  -H "X-Moov-Version: v2026.07.00" \
  -H "X-Idempotency-Key: 7e1d4a2b-6c93-4f08-b5d2-1a9e3c8f6b70" \
  -d '{ "amount": 1000 }'
```

Rules for refunds:

- You can't refund more than the original amount, and only one refund can be in progress at a time for each transfer.
- Refunds can't be canceled once issued. If a refund fails, you can try again on the same transfer.
- The refund amount is debited from the merchant's wallet immediately. The cardholder usually sees a pending credit right away that posts in 5-7 business days, depending on their bank.
- If the payment included a [surcharge](/guides/accept-payments/cards/surcharges/index.md#refund-a-surcharged-payment), a full reversal refunds the surcharge automatically. For a partial refund, use the refunds [endpoint](/api/money-movement/refunds/create/index.md) and specify the proportional surcharge in `amountDetails`.

Deeper guide: [refunds](/guides/accept-payments/cards/refunds/index.md).

### Cancel an ACH transfer

ACH transfers can be canceled only before the relevant leg originates: the debit for bank-to-wallet transfers, and the credit for wallet-to-bank and bank-to-bank transfers. Call the cancel a transfer [endpoint](/api/money-movement/transfers/cancel/index.md):

```zsh
curl -X POST "https://api.moov.io/accounts/{partnerAccountID}/transfers/{transferID}/cancellations" \
  -H "Authorization: Bearer {token}" \
  -H "X-Moov-Version: v2026.07.00" \
  -H "X-Idempotency-Key: c4f8a1d2-3e57-4b96-8a0c-2d6e9f1b7a53"
```

The response has a `cancellationID` and a `status` of `pending`, `completed`, or `failed`. For a bank-to-bank transfer whose debit already originated, the cancellation stays `pending` while Moov waits for the debit to complete, then credits the source wallet instead of sending the credit leg. If the transfer already originated, failed, or completed, you get a `400`. Requests made very close to origination can also fail. Check progress with the cancellation details [endpoint](/api/money-movement/transfers/cancel-details/index.md).

Canceling a transfer in a [transfer group](/guides/money-movement/transfer-groups/index.md) also cancels the transfers after it in the group. It doesn't affect future [scheduled transfers](/guides/money-movement/scheduling/index.md). Deeper guide: [ACH cancellations](/guides/money-movement/ach/cancellations/index.md).

### Return funds after an ACH payment originates

Once an ACH payment originates, you can't cancel it, and the refunds endpoint only supports card transfers. ACH returns are started by the bank, not by you. To give a customer their money back, create a new transfer in the opposite direction with the create transfer [endpoint](/api/money-movement/transfers/create/index.md): from the wallet that received the funds to an ACH credit payment method on the customer's bank account.

```zsh
curl -X POST "https://api.moov.io/accounts/{partnerAccountID}/transfers" \
  -H "Authorization: Bearer {token}" \
  -H "X-Moov-Version: v2026.07.00" \
  -H "X-Idempotency-Key: 9a2e6c1f-4b38-4d75-a0e9-8f3b1c5d2a64" \
  -d '{
    "source": { "paymentMethodID": "{merchantWalletPaymentMethodID}" },
    "destination": { "paymentMethodID": "{customerAchCreditPaymentMethodID}" },
    "amount": { "currency": "USD", "value": 2500 },
    "description": "Refund for order 1042"
  }'
```

Find both payment method IDs with the list payment methods [endpoint](/api/sources/payment-methods/list/index.md). Use `ach-credit-standard` or `ach-credit-same-day` for the destination, or `instant-bank-credit` if the customer's bank supports instant payments. Use `foreignID` or `metadata` on the transfer to link it to the original payment in your records. Deeper guide: [send funds](/guides/send-payments/send-funds/index.md).

### Track the outcome

Each action has its own events:

- **Cancellations:** `cancellation.created`, then `cancellation.updated` with `status` `completed` or `failed`. The transfer's `status` becomes `canceled`, and card transfers also show `source.cardDetails.status` `canceled`.
- **Refunds:** `refund.created`, then `refund.updated` with `status` `pending`, `completed`, or `failed`. The refund's `cardDetails.status` moves through `initiated`, `confirmed`, `settled`, and `completed`.
- **Reversals:** produce cancellation or refund events, depending on which action Moov took, plus `transfer.updated`.

To check a refund directly, use the retrieve refund [endpoint](/api/money-movement/refunds/get/index.md):

```zsh
curl -X GET "https://api.moov.io/accounts/{partnerAccountID}/transfers/{transferID}/refunds/{refundID}" \
  -H "Authorization: Bearer {token}" \
  -H "X-Moov-Version: v2026.07.00"
```

If a refund fails, `cardDetails.failureCode` explains why, such as `suspected-fraud`. A `4xx` response to a reversal request means the card network declined it or a processing error occurred; retry with a new idempotency key. Deeper guide: [reversal settlement status](/guides/accept-payments/cards/reversals/index.md#track-the-outcome).

## What to watch for

| Signal                                                              | What it means                                              | What to do                                                                              |
|---------------------------------------------------------------------|------------------------------------------------------------|-----------------------------------------------------------------------------------------|
| Reversal response contains `cancellation` with `status` `completed` | The card authorization was reversed before settlement      | Mark the order canceled; no refund will appear on the statement                         |
| Reversal response contains `refund` with `status` `pending`         | The payment had started settling, so Moov issued a refund  | Wait for `refund.updated`                                                               |
| `refund.updated` with `status` `completed`                          | The credit reached the card network                        | Mark refunded; the cardholder sees it within 5-7 business days                          |
| `refund.updated` with `status` `failed`                             | The card network declined the refund                       | Read `cardDetails.failureCode`; retry on the same transfer or return funds another way  |
| `cancellation.updated` with `status` `failed`                       | The ACH leg originated before the cancellation took effect | Wait for the transfer to complete, then send the funds back with a new transfer         |
| `400` from the cancellations endpoint                               | The transfer already originated, failed, or completed      | Use a reversal for cards, or a new transfer for ACH                                     |
| `409` from the refunds endpoint                                     | You reused an idempotency key                              | The response body contains the existing refund; don't create another                    |
| `dispute.created` on the transfer                                   | The cardholder disputed the charge                         | Don't refund; respond to the [dispute](/guides/accept-payments/cards/disputes/index.md) instead |

## Next steps

- [Reversals](/guides/accept-payments/cards/reversals/index.md): Full details on reversal responses, statuses, and webhooks.
- [ACH returns](/guides/money-movement/ach/returns/index.md): How bank-initiated returns reverse ACH payments.
- [Disputes](/guides/accept-payments/cards/disputes/index.md): Respond when a cardholder disputes a charge.
- [Handle webhooks end to end](/guides/get-started/recipes/webhooks/index.md): Process refund and cancellation events reliably.

