Prerequisites
- The original transfer is in
completedstate - You’re still within the refund window (default 90 days from transfer completion — configurable per tenant)
- The sending account still has sufficient balance, OR you use a different funding source
Full refund
Simplest case — return the full amount:amount → full refund.
Partial refund
Specify the amount to return:Reason codes
Reason codes feed analytics + dispute defensibility. Be specific.
Idempotency
Always setX-Idempotency-Key. Refunds are the one operation you absolutely do not want to accidentally double-process.
The key is cached 24 hours. Same key + same parameters → returns original result. Same key + different parameters → IDEMPOTENCY_KEY_REUSED.
Watch settlement
pending → completed | failed.
Or subscribe to webhooks: transfer.refunded fires on the original transfer when refund settles.
Failures
List refunds
Filter by status, account, or date range:- Stuck pending — refunds older than 1 hour still
pending - Recent failures — indicate wallet or rail issues upstream
- Volume trend — sudden spikes often precede disputes
Refund vs. dispute
- Refund = you voluntarily return money. Use when the buyer asks or you spot an error first.
- Dispute = buyer filed a formal complaint via card network or Sly. You can still issue a refund during a dispute — often the cleanest resolution.
Endpoints
Practice
In sandbox: send a small transfer, then immediately refund it. Check that your webhook handler receivestransfer.refunded and updates your ledger. Test partial refunds, double-refund attempts, and post-window rejection before going live.