Skip to Content

Flow: refund

A refund is a separate operation on top of an already paid invoice. No separate “refund invoice” is created: the original one changes its status, and the result arrives in a callback.

  1. Wait for the payment. A refund is accepted only with payment_status: PAID. Refunding an unpaid invoice returns 422 INVOICE_NOT_PAID immediately; nothing changes, and retrying is pointless.
  2. Request the refund: POST /invoices/{invoice_id}/refund with an empty body {}. The response is 202 with invoice_id and refund_status: processing.
  3. Wait for the refund.updated callback (refund.completed in the legacy envelope): the refund status arrives in data.invoice.refund_status. Deduplicate it by the event identifier like any other event (see “Callbacks”).
  4. Reconcile against the invoice: after a refund GET /invoices/{invoice_id} returns status: Refund with payment_status: PAID. The paid flag is not reset — the money was charged, and that fact stays in the history.

In the XML flow the refund arrives as the same SendPaymentTransaction. It has no separate name: a refund notification is recognized by the refund status in InvoiceInfo/InvoiceStatus and the absence of the PaymentTransactionInfo block; the sandbox refunds instantly and always successfully, so it sends the single status Refund. Details in “Callbacks”.

Request and response
// POST /api/v3/projects/1001/invoices/{invoice_id}/refund {} // 202 Accepted { "success": true, "data": { "invoice_id": "4c702dc3-176e-4365-ab4d-8fd22cbcfab1", "refund_status": "processing" } }
The invoice after a refund
// GET /api/v3/projects/1001/invoices/{invoice_id} { "status": "Refund", "payment_status": "PAID" }
Refunding an unpaid invoice
// 422 Unprocessable Entity { "success": false, "error": { "code": "INVOICE_NOT_PAID" } }