Credit notes
POST /v1/credit-notes credits items of an issued sales invoice and issues the credit note in one call. There is no draft step, and an issued credit note is never cancelled. The credited goods go back to stock.
Required fields
Every request sends invoiceId, reason and lines, with an Idempotency-Key header like every POST:
{
"invoiceId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"reason": "refund",
"lines": [{ "itemId": "7c8d9e0f-1a2b-4c3d-8e4f-5a6b7c8d9e0f", "quantity": 1 }],
"refund": { "paymentMethodId": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9" }
}
The credit note takes its branch, its customer and its prices from the invoice. Each line names an item sold on that invoice, once, with a whole quantity no larger than what is left of that item to credit. A unit is credited at its share of what the invoice charged for the item. On an invoice issued through the API, each line is rounded to the halala the way ZATCA totals the credit note, so crediting units one at a time can come to a halala more than crediting them together. Such a credit note may therefore pass what is left of the invoice by one halala for each credit line already issued on it, and always by at least one halala, so the last units can always be credited.
The full reply is in the API reference.
reason is sent to ZATCA as the reason for the credit note:
reason | Use it when |
|---|---|
cancellation | the sale is cancelled, fully or in part |
refund | goods or services are returned and the money is given back |
informationChange | the invoice's details were wrong and are being corrected |
notes takes up to 500 characters and is printed on the credit note. referenceId takes up to 100 characters and must not be used by any other invoice or credit note of the merchant.
Paying the money back
With refund, the credit note's total is paid back through refund.paymentMethodId, out of that method's treasury for the invoice's branch:
- A method whose treasury serves only another branch is refused (
422 payment_method_not_in_branch). - The refund cannot be more than the invoice has collected, less what was already paid back on it (
422 refund_exceeds_collected).
Without refund, the total comes off what the customer owes. The sales invoice's amountDue drops by it, never below 0, and whatever is left over stays as the customer's credit. Only a customer who can hold a balance can be credited this way:
- A company customer always can.
- An individual customer carries no balance, so they can be credited this way only up to what the invoice still owes. Above that, send
refund(400 invalid_parameteronrefund). - A sale with no customer needs
refund(400 invalid_parameteronrefund).
This holds for a sale paid by card at a till too: without refund, the credit goes to the customer's balance, and nothing is refunded to the card.
The reply shows the money paid back as refund.amount, or refund: null when the credit went to the customer's balance.
How much can be credited
The credits on an invoice never add up to more than its total, whatever channel issued them. Refunds made at the till or on the dashboard, sales returns, and credit notes issued through the API all count against the same total, and against the quantity left of each item. A credit above what is left answers 422 credit_exceeds_invoice:
paramislines[i].quantitywhen that line asks for more than is left of its item.paramislineswhen the credit note's total is more than is left to credit on the invoice.
Two credit notes for the same invoice sent at the same moment are issued one after the other, so the second one is checked against what the first one left. If the first one is still being issued after 30 seconds, for example while it waits for ZATCA, the second answers 409 invoice_busy. Send it again.
ZATCA
A credit note is the same kind of ZATCA document as its invoice:
- Standard invoice:
- The credit note is cleared by ZATCA before the reply.
- If ZATCA rejects it, nothing is saved and the reply is
422 zatca_rejectedwith ZATCA's messages inzatcaErrors. - If ZATCA does not answer in time, the reply is
201withzatca.status: "pending". Kosoor sends it again later, and it becomescleared, orrejectedwith ZATCA's messages inzatca.errors. Either way the credit note stays issued. - An invoice ZATCA has not cleared yet cannot be credited (
409 invoice_not_clearedoninvoiceId). Retry onceGET /v1/sales-invoices/{id}showszatca.statusascleared; that read needssales_invoices:read. A4xxreply is not stored, so the retry may keep itsIdempotency-Key.
- Simplified invoice: the credit note is reported to ZATCA after the reply.
zatca.statusispendinguntil then, and becomesreported, orrejectedwith ZATCA's messages inzatca.errors.
An invoice ZATCA rejected cannot be credited through the API (422 invoice_not_creditable). Correct the sale with the merchant first.
GET /v1/credit-notes/{id}/pdf and GET /v1/credit-notes/{id}/xml return the credit note's documents:
- A standard credit note's PDF and XML answer
409 invoice_not_cleareduntil ZATCA clears it, and for good if ZATCA rejects it. - A simplified credit note's PDF is ready straight away. Its XML answers
409 invoice_not_cleareduntil ZATCA has accepted the report, and for good if ZATCA rejects it.
Refusals
| Error | When |
|---|---|
422 referenced_resource_not_found | the invoice does not exist or is in a branch your key may not use, or the refund's payment method does not exist or is disabled; param names the field |
422 invoice_not_creditable | the invoice is not an issued sales invoice, it was imported, or ZATCA rejected it |
409 invoice_not_cleared on invoiceId | the invoice is standard and ZATCA has not cleared it yet |
400 invalid_parameter on lines[i].itemId | the item was not sold on this invoice |
422 credit_exceeds_invoice | a line asks for more than is left of its item, or the total is more than is left to credit |
400 invalid_parameter on refund | no refund was sent, and the customer cannot hold the credit as a balance |
422 payment_method_not_in_branch | the refund's payment method pays out of a treasury that serves only another branch |
422 refund_exceeds_collected | the refund is more than the invoice has collected |
422 used_subscription_not_refundable | the invoice sold a subscription whose visits are already used |
422 refund_method_not_supported | the sale was paid by card on the merchant's online store; a card refund cannot be started through the API |
422 loyalty_refund_blocked | the loyalty platform blocked the credit: points or stamps earned on the sale were already spent, the platform could not be reached, or it has not received the sale yet; the message says which. Retry once a few minutes later; if it is refused again, the merchant can issue it from the dashboard, where a supervisor may override |
409 reference_id_taken | another invoice or credit note already has this referenceId; existingId names it only when it is a credit note your key may read |
409 invoice_busy on invoiceId | another request held the invoice for more than 30 seconds; send the request again |
422 zatca_rejected | ZATCA rejected the credit note of a standard invoice; nothing is saved, and ZATCA's messages are in zatcaErrors |
422 credit_note_not_issued | a business rule with no code of its own refused the credit note; the message names the rule |
When a request is refused, no credit note is issued and nothing is saved.
Reading credit notes
GET /v1/credit-notes lists every issued credit note in the key's branches, whichever channel issued it, including refunds made at the till or on the dashboard. createdVia says which. Filter with invoiceId, customerId, branchId, referenceId, issuedAfter, issuedBefore and zatcaStatus.
Sales returns and credit notes made on the dashboard's accounting screens are not listed, though they count against what is left to credit on the invoice.
refund.paymentMethodId is null when the refund named no payment method, as till refunds usually do: the money went back through the branch's own till, or to the card the sale took. A till refund split between cash and card shows one refund with the two added together.
A refund made before credit notes recorded a reason shows reason: "cancellation", the reason it was sent to ZATCA with.