# Reversals

> Reverse a card payment with one endpoint: Moov cancels the authorization if the payment hasn't settled, or issues a full or partial refund if it has.

> 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

A reversal returns money to the cardholder with one call. Moov checks the payment's settlement state, then cancels it if it can or refunds it if it can't.

Use reversals as the default way to undo card payments. Call it with your partner account's `/accounts/{accountID}/transfers.write` scope. For ACH, see [cancel an ACH transfer](/guides/money-movement/ach/cancellations/index.md); ACH has no refunds.

| Card payment state      | Request      | What Moov does                                                   | Resulting status    | Confirm with                               |
|-------------------------|--------------|------------------------------------------------------------------|---------------------|--------------------------------------------|
| Authorized, not settled | No `amount`  | Cancels the authorization, so the charge drops off the card      | Transfer `canceled` | `transfer.updated`, `cancellation.updated` |
| Settling or settled     | No `amount`  | Issues a full refund to the card                                 | Refund `completed`  | `refund.updated`                           |
| Any                     | `amount` set | Issues a partial refund (partial cancellations aren't supported) | Refund `completed`  | `refund.updated`                           |

Canceling costs less and returns funds faster than refunding. A refund debits the merchant's wallet immediately, and the cardholder usually sees the credit post in 5 to 7 business days.

## Reversal rules

- A reversal cannot exceed the original transfer amount
- Once requested, reversals cannot be canceled
- Avoid requesting a reversal for a payment disputed by the cardholder to prevent a potential double charge
- If a reversal request fails, you can initiate it again for the same transfer
- If a reversal includes a [surcharge](/guides/accept-payments/cards/surcharges/index.md), the surcharge must be specified in the request

## Initiate a reversal

Call the reversals `POST` [endpoint](/api/money-movement/refunds/cancel/index.md). `amount` is required: set it to the full transfer amount for a full reversal, or to a smaller amount for a partial refund. With a partial refund, the cardholder sees the full charge followed by a partial credit.

**Full refund**

```zsh
curl -X POST "https://api.moov.io/accounts/{accountID}/transfers/{transferID}/reversals" \
  -H "Authorization: Bearer {token}" \
  -H "x-idempotency-key: UUID" \
  -H "x-moov-version: v2026.07.00" \
  --data-raw '{
    "amount": 2500
  }'
```

**Partial refund**

```zsh
curl -X POST "https://api.moov.io/accounts/{accountID}/transfers/{transferID}/reversals" \
  -H "Authorization: Bearer {token}" \
  -H "x-idempotency-key: UUID" \
  -H "x-moov-version: v2026.07.00" \
  --data-raw '{
    "amount": 1000
  }'
```

> **Note:** For immediate error correction, we recommend requesting a full reversal and creating a new transfer to create a smoother cardholder experience.

You can also initiate a reversal through the Moov [Dashboard](/guides/money-movement/overview/index.md#refund-reverse-or-cancel-a-transfer).

## Initiate a cancellation

While a reversal will cancel the transaction if possible, you also have the option of requesting a cancellation directly with the cancel a transfer `POST` [endpoint](/api/money-movement/transfers/cancel/index.md).

**Request a cancellation**

If you request a cancellation, but the card network has already settled, you'll receive an error. If the cancellation window has passed, use the [reversals](#initiate-a-reversal) or [refunds](/guides/accept-payments/cards/refunds/index.md) endpoint instead.

```zsh
curl -X POST "https://api.moov.io/accounts/{accountID}/transfers/{transferID}/cancellations" \
  -H "Authorization: Bearer {token}" \
  -H "x-idempotency-key: UUID" \
  -H "x-moov-version: v2026.07.00"
```

**Retrieve a cancellation**

You can get the details of a cancellation using the cancellation details `GET` [endpoint](/api/money-movement/transfers/cancel-details/index.md).

```zsh
curl -X GET "https://api.moov.io/accounts/{accountID}/transfers/{transferID}/cancellations/{cancellationID}" \
  -H "Authorization: Bearer {token}" \
  -H "x-moov-version: v2026.07.00"
```

## Track the outcome

The reversal response tells you synchronously whether Moov canceled or refunded the payment. Follow it with the [webhooks](#webhooks) below, or the [cancellation](/api/money-movement/transfers/cancel-details/index.md) or [refund](/api/money-movement/refunds/get/index.md) `GET` endpoints.

**Cancellation**

```json
{
  "cancellation": {
    "cancellationID": "string",
    "createdOn": "2024-08-24T14:15:22Z", 
    "status": "completed"	
  } 
}
```

A cancellation typically completes instantly. However, if there's an error with the card network response, a `pending` status is returned until a confirmation is received.

A `4xx` error after reversal initiation signifies a decline from the card networks or a processing error. Retry the request with a new idempotency key. See our [reversal](/api/money-movement/refunds/cancel/index.md) API documentation for further information on HTTP error codes.

**Refund**

```json
{
  "refund": { 
    "refundID": "ec7e1848-dc80-4ab0-8827-dd7fc0737b43", 
    "createdOn": "2024-08-24T14:15:22Z",
    "updatedOn": "2024-08-24T14:15:32Z", 
    "status": "pending",
    "amount": {
      "currency": "USD",
      "value": 1204
    },
    "cardDetails": {
      "status": "confirmed",
      "confirmedOn": "2023-09-10T18:23:56Z"
    }
  }
}
```

A [refund](/api/money-movement/refunds/get/index.md) will have a `pending` status while waiting for authorization, or a `failed` status if it was declined by the card networks. The refund's `cardDetails.status` will update with one of the following detailed rail specific statuses:

| Rail-specific status | Description                                  |
|----------------------|----------------------------------------------|
| `initiated`          | Refund has been initiated by the user        |
| `confirmed`          | Refund has been accepted by the card network |
| `settled`            | Refund has been settled                      |
| `completed`          | Refund has completed                         |
| `failed`             | Refund has failed                            |

If the `refund` status updates to `failed`, the `cardDetails` object will update with a failure code.

```json
{
  "refund": { 
    "refundID": "ec7e1848-dc80-4ab0-8827-dd7fc0737b43", 
    "createdOn": "2024-08-24T14:15:22Z",
    "updatedOn": "2024-08-24T14:15:32Z", 
    "status": "failed",
    "amount": {
      "currency": "USD",
      "value": 1204
    },
    "cardDetails": {
      "status": "failed",
      "failureCode": "suspected-fraud",
      "failedOn": "2023-09-10T18:23:56Z"
    }
  }
}
```

If a transfer has a refund or cancellation in any status, the transfer response includes it too.

```json
{
  "transferID": "string",
  "createdOn": "2024-08-24T14:15:22Z",
  "status": "canceled",
  "cancellations": [
    {
      "cancellationID": "string",
      "createdOn": "2023-09-09T14:15:22Z",
      "status": "completed"
    }
  ]
}
```

## Webhooks

Subscribe to the following webhook event, which will provide you with relevant *reversal* updates:

- `transfer.updated` notifies you once the reversal status changes to any of the following:
  
  - `canceled`
  - `source.canceled`

Subscribe to the following webhook events, which will provide you with relevant *cancellation* updates:

- `cancellation.created` notifies you when the cancellation was successfully created
- `cancellation.updated` notifies you when the cancellation was updated
  
  - `pending`
  - `completed`
  - `failed`

Subscribe to the following two webhook events, which will provide you with relevant *refund* updates:

- `refund.created` notifies you when the refund was successfully created
- `refund.updated` notifies you once the refund status changes to any of the following:
  
  - `pending`
  - `completed`
  - `failed`

