Refund, reverse, or cancel a payment

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: 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 creates a card payment; the wallet funding recipe creates an ACH debit.
  • Webhooks subscribed to transfer.updated, refund.created, refund.updated, cancellation.created, and cancellation.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-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 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, 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:

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

Next steps