Refund, reverse, or cancel a payment
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: test mode API keys, authentication headers, your partner account ID, and a webhook endpoint
- Token scopes:
/accounts/{partnerAccountID}/transfers.readand/accounts/{partnerAccountID}/transfers.write - A completed or pending test transfer to undo. The accept a card payment recipe creates a card payment; the wallet funding recipe creates an ACH debit.
- Webhooks subscribed to
transfer.updated,refund.created,refund.updated,cancellation.created, andcancellation.updated. See handle webhooks end to end.
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.
Steps
Look up the transfer
Get the transfer with the get transfer endpoint to see its rail and state:
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-paymentfor cards;ach-debit-fundorach-debit-collectfor ACH debitssource.cardDetails.status:initiatedorconfirmedmeans the card payment hasn't settled, so it can still be canceled.settledorcompletedmeans it needs a refund.source.achDetails.statusanddestination.achDetails.status:initiatedmeans the ACH leg hasn't originated yet, so you can cancel;originatedor later means you can'tstatus:failedorcanceledtransfers have nothing to undo, andreversedmeans the funds already went backrefundedAmount,refunds, andcancellations: any earlier attempts on this transfer
Deeper guides: card payment timing and ACH statuses.
Reverse a card payment
For a full refund of a card payment, call the reversals endpoint without a body. An X-Idempotency-Key is required; reuse it if you retry the same request.
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.
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.
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, a full reversal refunds the surcharge automatically. For a partial refund, use the refunds endpoint and specify the proportional surcharge in
amountDetails.
Deeper guide: refunds.
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:
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.
Canceling a transfer in a transfer group also cancels the transfers after it in the group. It doesn't affect future scheduled transfers. Deeper guide: ACH cancellations.
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: from the wallet that received the funds to an ACH credit payment method on the customer's bank account.
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. 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.
Track the outcome
Each action has its own events:
- Cancellations:
cancellation.created, thencancellation.updatedwithstatuscompletedorfailed. The transfer'sstatusbecomescanceled, and card transfers also showsource.cardDetails.statuscanceled. - Refunds:
refund.created, thenrefund.updatedwithstatuspending,completed, orfailed. The refund'scardDetails.statusmoves throughinitiated,confirmed,settled, andcompleted. - 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:
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.
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 instead |