Reversals
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; 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, the surcharge must be specified in the request
Initiate a reversal
Call the reversals POST endpoint. 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.
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
}'
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
}'
You can also initiate a reversal through the Moov Dashboard.
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.
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 or refunds endpoint instead.
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"
You can get the details of a cancellation using the cancellation details GET endpoint.
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 below, or the cancellation or refund GET endpoints.
{
"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 documentation for further information on HTTP error codes.
{
"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 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.
{
"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.
{
"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.updatednotifies you once the reversal status changes to any of the following:canceledsource.canceled
Subscribe to the following webhook events, which will provide you with relevant cancellation updates:
cancellation.creatednotifies you when the cancellation was successfully createdcancellation.updatednotifies you when the cancellation was updatedpendingcompletedfailed
Subscribe to the following two webhook events, which will provide you with relevant refund updates:
refund.creatednotifies you when the refund was successfully createdrefund.updatednotifies you once the refund status changes to any of the following:pendingcompletedfailed