Refunds return funds from a successful payment back to the customer. SwissPay supports full and partial refunds, and a single payment can be refunded multiple times up to its captured amount.
The Refund object
{
"id": "re_01HABCXYZ...",
"payment": "pay_01HABCXYZ...",
"amount": 1000,
"currency": "CHF",
"status": "pending",
"reason": null,
"metadata": { "order_note": "wrong size" },
"created_at": "2026-05-20T12:00:00Z"
}
| Field | Type | Notes |
|---|---|---|
id |
string | Stable, prefixed with re_. |
payment |
string | The pay_... ID being refunded. |
amount |
integer | Minor units. |
currency |
string | ISO 4217. Always matches the original payment. |
status |
string | The refund's current state. See Refund lifecycle below. |
reason |
string | null | Optional free-text reason you supplied on creation. |
metadata |
object | Up to 50 string keys, values ⤠500 bytes. Echoed back on read. |
created_at |
string | ISO 8601 UTC. |
Create a refund
POST /api/v1/payments/{payment_id}/refunds
Required headers
Authorization: Bearer sk_test_...
Content-Type: application/json
Idempotency-Key: <unique-per-attempt>
Idempotency-Key is required â refunds move money and must be safe to retry. See Idempotency.
Body fields
All fields are optional. An empty body refunds the full remaining refundable amount of the payment.
| Field | Type | Notes |
|---|---|---|
amount |
integer | Minor units. Must be ⤠the payment's remaining refundable amount. Omit for a full refund. |
reason |
string | Free-text reason. Stored on the refund and returned on read. |
metadata |
object | Up to 50 keys, values ⤠500 bytes. Stored verbatim and echoed back on read. |
Example â full refund
curl -X POST https://app.swisspay.ai/api/v1/payments/pay_01HABCXYZ/refunds \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'
Example â partial refund with metadata
{
"amount": 1000,
"reason": "Item returned",
"metadata": { "order_note": "wrong size" }
}
Multiple partial refunds
A payment can be refunded more than once as long as the sum of refund amounts does not exceed the captured amount. Each call returns its own Refund object with its own id.
Refund lifecycle
A refund is created in pending while SwissPay forwards the request to the upstream processor. Once the processor confirms, the refund transitions to a terminal state. The refund's id is stable across the transition â poll GET /api/v1/refunds/{id} (or list refunds on the payment) to observe the final state.
Retrieve a refund
GET /api/v1/refunds/{id}
curl https://app.swisspay.ai/api/v1/refunds/re_01HABCXYZ \
-H "Authorization: Bearer sk_test_..."
Unknown IDs â including IDs that belong to another merchant â return 404 Not Found with no body.
List refunds for a payment
GET /api/v1/payments/{payment_id}/refunds?page=1&per_page=20
| Query param | Default | Max |
|---|---|---|
page |
1 |
â |
per_page |
20 |
100 |
curl 'https://app.swisspay.ai/api/v1/payments/pay_01HABCXYZ/refunds' \
-H "Authorization: Bearer sk_test_..."
Response:
{
"data": [ /* refund objects */ ],
"page": 1,
"per_page": 20,
"total_count": 2,
"has_more": false
}
See also
- Payments â the resource refunds operate on.
- Idempotency â pick the right
Idempotency-Key. - Errors â every error code we return.
Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article