Skip to main content

Refund a Payment

Requires the payments:refund scope, which is separate from payments:capture — an application needs to be granted refund access explicitly.

Refund everything

curl -X POST https://dev.gladys.the-all.io/api/v1/payments/pay_abc123/refunds \
-H "Authorization: Bearer <your access token>" \
-H "Idempotency-Key: <a unique key for this request>" \
-H "Content-Type: application/json" \
-d '{"reason": "Customer requested a refund"}'

Omitting amount refunds everything not already refunded — you don't need to compute a remainder yourself.

Refund part of it

curl -X POST https://dev.gladys.the-all.io/api/v1/payments/pay_abc123/refunds \
-H "Authorization: Bearer <your access token>" \
-H "Idempotency-Key: <a unique key for this request>" \
-H "Content-Type: application/json" \
-d '{"amount": 50000, "reason": "Partial return"}'

A payment can be refunded more than once — each partial refund is its own object with its own reference, so you can tell them apart later. List them with:

curl https://dev.gladys.the-all.io/api/v1/payments/pay_abc123/refunds \
-H "Authorization: Bearer <your access token>"

Rules to design around

  • Only a CAPTURED payment can be refunded. An authorized-but-not-yet-captured hold should be cancelled instead (see Accept a Payment).
  • You can't over-refund. Requesting more than what's still refundable (amount - already refunded) fails with a 400 — it never partially applies.
  • The fee isn't reversed. The payer is credited the full refunded amount, but the original transaction fee stays with Gladys — a fully-refunded payment leaves you short exactly that fee. Account for this in your reconciliation rather than assuming a full refund zeroes out.
  • A refund fires its own webhook. payment.refunded includes a refund object naming which refund it was — see Receive Webhooks.

Always send an Idempotency-Key on refund calls — retrying an unacknowledged refund request without one risks issuing it twice.