ZyloPaydocs

Refunds

Full and partial refunds, where the money comes from, the limits that apply, and how idempotency keeps a refund from being sent twice.

A refund (re_…) returns all or part of a payment's amount to the customer's wallet, in USDC, on the payment's own network. You can refund from the API, the dashboard or the POS terminal; all three share one ledger, so a payment can never be refunded beyond its amount.

Full and partial

// Partial: 1.00 USDC of a 2.50 USDC payment
await zp.refunds.create({ payment: 'pay_1042', amount: 1000000 }, { idempotencyKey: `refund-${returnId}` });

// Full (whatever is left): omit amount
await zp.refunds.create({ payment: 'pay_1042' }, { idempotencyKey: `refund-${returnId}` });
  • You can refund several times until amount_refunded equals amount. The payment moves to partially_refunded, then refunded.
  • Asking for more than what is left answers 400 amount_too_large. A fully refunded payment answers 409 payment_already_refunded.
  • The network fees the customer paid are not refunded. A refund returns at most the sale amount.

Where the money comes from

Refunds are paid from your merchant balance in the ZyloPay settlement contract (see Settlement). If you have withdrawn the funds, the balance must cover the refund, or the refund fails and nothing moves.

Limits

LimitDefaultError
Refund window after settlement90 days409 refund_window_expired
Refunds per day, per account and network10,000 USD409 refund_daily_limit_reached
One refund in flight per payment409 refund_in_progress (retry after it completes)

Contact ZyloPay to change the window or the daily limit. ZyloPay also screens the recipient; a refused refund answers 403 account_restricted and nothing is sent.

Statuses

The call waits for the transaction receipt, so it usually answers with the final outcome.

StatusMeaningWhat to do
succeededMined. The funds are with the customer.Done.
pendingSent, but the receipt was not seen in time.Do not retry with a new key. It resolves on its own; wait for refund.succeeded or refund.failed.
failedNothing moved. failure_code is refund_failed or settlement_reverted.Fix the cause (for example your balance) and create a new refund.

Idempotency

Idempotency-Key is required. Use a key tied to your own refund record, for example refund-<your return ID>:

  • A retry with the same key and body replays the first response, even an error. It never sends a second refund.
  • If the first request is still running, the retry answers 409 idempotency_request_in_progress. Wait and retry with the same key.
  • If you are unsure whether a refund happened, list the payment's refunds (GET /v1/refunds?payment=pay_…) before trying anything else.

See Handling refunds for the full integration pattern.

Events

refund.created (status pending, before the transaction is sent), then refund.succeeded with payment.refunded, or refund.failed.

On this page