Skip to main content
POST
Refunds money from a successful activity back to the payer. The refund is submitted to the provider before the call returns, so status comes back as processing, or failed if the provider refused it outright. Read Refunds first, in particular how much you can refund and idempotency.
The activity must be successful or partially_refunded. A failed charge, one still waiting on the customer, or a fully refunded one is rejected with 405.

Body parameters

string
required
The activity to refund, act.XXXX.YYYY.... Must belong to your workspace and hold settled money.
integer
Amount to refund, in the smallest currency unit (kobo for NGN). Omit it to refund everything still refundable on the activity.Must be at least the currency minimum (20000 = ₦200.00 for NGN), and cannot take the total past what the activity collected.
string
Your own reference, 1-150 characters. Unique per workspace, so reusing one returns 409. Strongly recommended on every refund: it is your idempotency key.
string
Why you’re refunding, 1-255 characters. Forwarded to the provider as the merchant note, so it shows up in their dashboard too.
string
Provider connection to refund through, psp.XXXX.YYYY.... Defaults to the connection that took the payment. It must be active and belong to the same provider as the original charge.
object
The customer’s bank account to send the money to. Required by some PSPs for bank_transfer and ussd payments, where the original payer account isn’t known; without it the refund fails. See Refunds for bank transfer and USSD payments.
object
Up to 50 string key/value pairs (keys ≤ 100 chars, values ≤ 255; null values are dropped). Stored against the refund for your own bookkeeping. The merchant API does not return it.

Response

The Refund object. The fields worth branching on immediately:
string
Refund ID, rfd.XXXX.YYYY.... Persist it: every refund webhook identifies itself with this.
string
processing on the happy path, failed if the provider refused it outright. Do not treat processing as money returned; wait for refund.successful.
object | null
Populated when status is failed.
object
The activity, already updated. Read refunded_amount here to see how much of the payment is now committed to refunds.

Errors

A failed refund is a 200 response, not an HTTP error. A provider declining a refund is a normal outcome, so it comes back as a refund object with "status": "failed" and a populated error. Check status, not just the HTTP code.