n1coePayDocs API reference

Refunds

Return the full amount of a payment from your server and follow its result with webhooks.

  1. Your server refunds a paid payment intent with POST /v1/refunds.
  2. Check the status of the response: succeeded, failed or pending.
  3. Confirm the result with the refund.succeeded and refund.failed webhooks.
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 POST sends 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. Its refund field has the refund id.

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.