Refund a card charge

Request a full or partial refund and confirm when funds have been returned.

📘

API Refs

POST /api/public/card-charges/{id}/refunds

Include your API key in the Authorization header and send JSON. Funds return to the original card, not an alternative bank account or payment method.

Request body

NameTypeDescriptionRequired
amountintegerPrincipal to refund in cents, excluding surcharge. Omit it to refund the remaining refundable principal in full. Must be positive and cannot exceed the remaining refundable amount.no
idempotencyKeystringUnique key for this refund, up to 255 characters. Reuse the same key when retrying this request. Strongly recommended to avoid duplicate attempts.no

Full refund request

{
  "idempotencyKey": "order-123-full-refund"
}

For a partial refund, supply amount. Any original surcharge is refunded proportionally, with the final portion taking the remaining surcharge to account for rounding. Do not add the surcharge to the requested principal yourself.

Pending is not completed

HTTP 201 means the refund request was accepted or an existing refund was returned on replay. Inspect the returned status; an accepted request may still be processing.

Illustrative pending response for a $1 refund:

{
  "id": 9,
  "chargeId": 14,
  "status": "processing",
  "refunded": {
    "principalCents": 0,
    "surchargeCents": 0,
    "totalCents": 0
  },
  "failureCode": null,
  "failureMessage": null,
  "createdAt": "2026-09-11T02:48:03.114Z"
}

While a refund is processing, refunded contains zero confirmed returned funds. A previously succeeded charge remains succeeded unless another confirmed refund has already changed its state. Pending amounts are reserved against the refundable balance, but are not reported as returned. Failed refunds also report zero confirmed returned funds.

Confirm completion

Subscribe to card_charge_refund_status_changed (event ID 8) and card_charge_status_changed (event ID 7). When the refund completes, the refund status is succeeded and its confirmed returned amounts become nonzero. The charge becomes refunded for a full refund or partially_refunded for a partial refund.

The refund webhook includes both the original receipt and the separate confirmed refunded amounts. GET the charge to retrieve its refund history and reconcile current state with Moov. Processing time is asynchronous; do not assume completion immediately after POST or after a fixed delay.

Illustrative completed refund, as returned within GET's refunds array or by a successful same-key replay:

{
  "id": 9,
  "chargeId": 14,
  "status": "succeeded",
  "refunded": {
    "principalCents": 100,
    "surchargeCents": 0,
    "totalCents": 100
  },
  "failureCode": null,
  "failureMessage": null,
  "createdAt": "2026-09-11T02:48:03.114Z"
}

Retry safely and handle failures

Repeat the same refund request using the same idempotencyKey. A successful replay preserves the original refund id and createdAt and returns the current stored status rather than a frozen copy of the first response. Linked pending refunds are refreshed on a best-effort basis during replay; use GET when you need the provider-validation read. Replays can therefore show succeeded after the original response showed processing, without creating another refund.

ResultWhat to do
HTTP 400Correct invalid input, such as a nonpositive or noninteger amount.
HTTP 404Check the charge ID and the account associated with your API key.
HTTP 422The refund was rejected or is not allowed, for example when the charge is already fully refunded. Inspect the error before creating another attempt.
HTTP 502 with code: "REFUND_OUTCOME_UNKNOWN"The processor outcome is uncertain. Do not assume no money moved and do not switch to a new key. Reuse the original key and reconcile or contact support if the outcome remains unresolved.

Processor-rejected refunds use REFUND_REJECTED; failed refunds without a more specific code use REFUND_FAILED. A replay of a failed or unresolved attempt returns the corresponding handled error rather than a successful refund response. Validation errors may have a message without a refund failure code.


Did this page help you?