Refunds
Refund a succeeded payment session with
POST /gateway/sessions/{id}/refund/.
Refunds can be full or partial, and a session can be refunded several times
until its whole amount has been returned.
Request
Section titled “Request”curl https://api.paymee.online/api/v1/gateway/sessions/ps_0b7c3a1e-…/refund/ \ -H "Authorization: Bearer sk_live_…" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 6f1c0e9a-2d4b-4b8e-9a51-0c3f7d2e8b14" \ -d '{ "reason": "Damaged item", "refund_type": "PARTIAL", "amount": "3.000" }'| Field | Type | Required | Notes |
|---|---|---|---|
reason |
string (≤ 500) | ✅ | Why the refund is issued. |
refund_type |
string | – | FULL (default): the whole amount of the payment. PARTIAL: the amount you give. |
amount |
string | for PARTIAL |
A positive decimal string in TND, e.g. "3.000". Extra decimals are rounded to the millime. |
metadata |
object | – | Your own key/values, stored with the refund. |
A FULL refund always means the full amount of the payment. After a partial refund,
return the rest with a PARTIAL refund of the remaining amount.
Retrying safely: Idempotency-Key
Section titled “Retrying safely: Idempotency-Key”A refund request that times out may or may not have been processed. To retry without
refunding twice, send an Idempotency-Key header: a unique value you generate per
refund (a UUID v4 is ideal), up to 255 characters.
- Repeating a request with the same key and the same body returns the refund that
key created — with the
Idempotent-Replayed: trueheader — instead of refunding again. - If that refund had been refused (for example
insufficient-funds), repeating it with the same key tries again. - Using a key that was already used for a different refund request (another body or
another session) returns
409idempotency-key-reuse.
Keys do not expire. Without the header, every request is a new refund: a retried
PARTIAL request would refund twice. (Retrying a FULL refund without a key is refused
as over-refund, so it is safe either way.)
The refund object
Section titled “The refund object”A refund request answers 200 OK with the refund:
{ "id": "re_4f1d0c2e-9b7a-4c51-8f0e-2a6b1c3d4e5f", "session_id": "ps_0b7c3a1e-5d2f-4e8a-9c6b-1f2e3d4c5b6a", "merchant_reference": "order-0001", "amount": "3.000", "currency": "TND", "refund_type": "PARTIAL", "status": "SUCCEEDED", "reason": "Damaged item", "processor_message": null, "created_at": "2026-11-21T09:12:01.870000Z", "completed_at": "2026-11-21T09:12:03.095000Z"}status |
Meaning |
|---|---|
SUCCEEDED |
The money is on its way back to the buyer’s card. completed_at is set. |
PENDING |
The refund is accepted but could not be confirmed automatically; it is completed manually and can take a few days. processor_message says why. We contact you if anything is needed on your side. |
FAILED |
The money was not returned. completed_at is set and processor_message says why. |
Most card refunds are SUCCEEDED straight away. A PENDING refund ends as SUCCEEDED
or FAILED, and you are told by webhook: refund.succeeded or refund.failed
(see Webhooks). These events are sent for every refund,
including those issued from your dashboard.
Every refund of a session is also listed, oldest first, in the refunds array of
GET /gateway/sessions/{id}/ — use it for
reconciliation. A refund does not change the session’s own status, which stays
SUCCEEDED.
processor_message is informational, for your logs and support; don’t branch on its
text.
- Only a
SUCCEEDEDsession can be refunded. Refunds are always in TND. - The total of a session’s refunds can never exceed what was paid. A refund over the
remaining amount is refused (
over-refund). - A partial refund can be refused until the payment has settled, normally the day after
the payment (
partial-requires-settled). A full refund is accepted right away. - A refund can be refused when it can’t be funded from your balance
(
insufficient-funds).
Errors
Section titled “Errors”Errors are problem documents. A refusal changes nothing: you can fix the cause and send the request again.
type suffix |
HTTP | When |
|---|---|---|
bad-request |
400 | The body isn’t valid JSON, or the Idempotency-Key is empty or over 255 characters. |
not-found |
404 | No such session on this account. |
idempotency-key-reuse |
409 | The Idempotency-Key was already used for a different refund request. |
validation-error |
422 | reason missing, unknown refund_type, or a PARTIAL refund without a positive amount. |
invalid-state |
422 | The session or its payment can’t be refunded (e.g. the session isn’t SUCCEEDED). |
no-payment |
422 | The session has no successful payment. |
partial-requires-settled |
422 | Partial refund before the payment has settled. |
over-refund |
422 | More than the amount left to refund. |
insufficient-funds |
422 | The refund can’t be funded from your balance. |
refund-failed |
422 | The refund could not be recorded; nothing was returned to the buyer. |

