Skip to main content
POST
A refund sends up to the payment’s credited net back to an address the payer gives you. The platform fee is never refunded, and the refund’s own network fee comes out of the amount sent.
A refund is a new outbound transfer — crypto transfers are irreversible. Always confirm the destination address with the payer before you send.

Authorization

There are two ways to authorize a refund, depending on where the call comes from:
  • Dashboard (JWT): requires a merchant role (merchant_admin, merchant_member, or super_admin), the refunds:manage team permission, an active merchant account, and a fresh strong factor — passkey, TOTP, or an emailed code — in the request body. A member without refunds:manage is refused with 403 before any MFA is evaluated; a suspended merchant cannot refund at all. Request the emailed code via Send refund email code.
  • API key: requires the elevated, opt-in refunds:create scope and an active merchant account. There is no per-call MFA — key possession plus the scope is the authorization (the Stripe model), optionally narrowed by the key’s IP whitelist if you configure one (strongly recommended for refunds:create keys: an empty whitelist accepts the key from any IP). Grant this scope only to hardened backends. Every API-key refund also raises a dashboard notification to the merchant.
Rate limit: 10/min.

Parameters

string
required
ID of the payment to refund. The payment must be settled — settlement (funds moving to storage, shown as “Processing” in the dashboard while under way) must have completed.
string
required
Decimal string (preferred) or number. Must be less than or equal to the net credited minus prior refunds.
string
Payer’s address, 4–256 characters (API naming). One of destinationAddress or address is required.
string
Same destination, 4–256 characters (dashboard naming). The API path conventionally sends destinationAddress; both are accepted.
string
Always send this header on the API path. A retry with the same key normally replays the original refund instead of paying twice — but the replay is status-aware: if the original refund failed before broadcast, retrying the same key returns 409 REFUND_FAILED_RETRY_NEW_KEY, and you must retry with a fresh key (do not loop on the same one). Format: 1–255 printable ASCII characters, no spaces — a non-conforming key is rejected with 400 INVALID_IDEMPOTENCY_KEY.
These fields apply only on the dashboard (JWT) path. One of them is required as the fresh strong factor.
string
The single-use emailed code (see Send refund email code).
string
TOTP code — an alternative to emailCode / passkey.
object
WebAuthn assertion. Sent together with challengeKey.
string
Accompanies passkeyResponse.
string
Echo only — ignored; the refund is always in the payment’s currency.
On either auth path, every refund is checked against: settled payments only, an atomic per-payment refund cap, address format validation, tenant scoping (you can only refund your own payments), and idempotent egress (the outbound transfer is never executed twice for the same request).

Response

string
The refund’s ID. Poll it via Get a refund.
string
The payment this refund belongs to.
string
Always the payment’s currency (any currency sent in the body is ignored).
string
The requested refund amount (decimal string).
string
Amount actually sent to the destination after deducting the network fee.
string
The refund’s own network fee, deducted from the amount sent.
string
The payer-supplied destination address.
string
Starts at pending; advances to refunded (with txHash once verified on-chain) or failed — see Get a refund.

Errors

Refunds are funded from your current available balance, not from a hold on the original payment — so INSUFFICIENT_FUNDS can occur even when the request is within the refundable cap (for example, after you withdrew the balance). Branch on the code, never on message text. The egress path can also pass through FEE_ESTIMATE_FAILED, NO_SETTLEMENT_WALLET, and INVALID_DESTINATION.

See also