Refunds
Return the full amount of a payment from your server and follow its result with webhooks.
- Your server refunds a paid payment intent with
POST /v1/refunds. - Check the
statusof the response:succeeded,failedorpending. - Confirm the result with the
refund.succeededandrefund.failedwebhooks.
Refund a payment
curl -s -X POST https://api-epay.dev.n1co.dev/v1/refunds \
-H "Authorization: Bearer $N1CO_SECRET_KEY" \
-H 'Content-Type: application/json' \
-d '{
"payment_intent": "pi_UGF5bWVudEludGVudEV4YW1w",
"reason": "Duplicate order",
"metadata": { "ticket": "t-8812" }
}'201 Created
{
"id": "re_UmVmdW5kRXhhbXBsZTAwMDAwMDAx",
"object": "refund",
"payment_intent": "pi_UGF5bWVudEludGVudEV4YW1w",
"amount": 1250,
"currency": "USD",
"status": "succeeded",
"reason": "Duplicate order",
"metadata": { "ticket": "t-8812" },
"last_error": null,
"attempts": 1,
"created_at": "2026-10-18T10:00:00Z",
"updated_at": "2026-10-18T10:00:02Z",
"succeeded_at": "2026-10-18T10:00:02Z",
"failed_at": null
}Rules#
- Full amount. There are no partial refunds and you do not send
amount. - One per payment. Repeating the
POSTsends the same refund again or returns an error; it never creates another one. - Only from your server. Neither n1co.js nor the buyer sees refunds.
- The intent stays
succeeded. Itsrefundfield has the refundid.
The result#
The call waits for n1co for up to about 45 seconds: use a timeout of at least 60.
status |
Meaning |
|---|---|
succeeded |
n1co returned the amount. It is final. |
failed |
n1co refused it; last_error says why. |
pending |
There is no result yet. |
The last_error and HTTP error codes are in Errors.
Send it again#
To retry a failed or pending refund, repeat the POST with the same payment_intent. The same refund is sent again and the response is 200.
If the refund is being sent at that moment, the response is 409 refund_in_progress with Retry-After.
Unknown outcome#
If n1co's answer could not be known, the refund stays pending with outcome_unknown and there is no webhook. After 3 minutes you can send it again.
Read and receive the result#
GET /v1/refunds/{id} returns the refund. The webhooks carry the refund in data.object:
| Event | When |
|---|---|
refund.succeeded |
Once, when n1co returns the amount. |
refund.failed |
For every refused send. |
The refund fields are in the API reference.