- Build Your Integration
Correct or cancel a document
Stage 5 of 6. Your app cannot change a document after Wafeq ASP accepts it. Before you go live, add the ways to reduce, cancel and correct a document.
This page uses these terms:
- Document: an invoice or a credit note that your app submitted, and that Wafeq ASP accepted.
- Document ID: the ID that Wafeq ASP gives a document when it accepts it (
document_id, for exampleinv_0123456789ABCDEFGHIJ). - Original: the document that a credit note, a void or a correction points at.
- Credit note: a document that reduces or cancels an invoice. It is a tax credit note (381) or a commercial credit note (81).
- Report: the copy of the document that Wafeq ASP sends to the FTA, the UAE Federal Tax Authority.
- Provider of the buyer: the e-invoicing provider that receives documents for the buyer, on Peppol.
- Withdrawal: a message from Wafeq ASP to the FTA that cancels a report. Only Wafeq ASP sends withdrawals.
- Void: an API call that cancels all of an invoice. Wafeq ASP makes the credit note for your app.
- Correction: a new version of a document that the provider of the buyer rejected. The correction has the same number as the original.
Three examples show when to use each way. In each example, Al Noor Trading LLC is the taxpayer, and Gulf Fresh Foods LLC is the buyer.
Example 1: the buyer returns goods. Al Noor sells 10 office chairs on invoice INV-2026-0410. The buyer returns 2 chairs. Your app issues a credit note for the 2 chairs, with the reason DL8.61.1.D.
Example 2: the buyer cancels the order. Wafeq ASP delivers invoice INV-2026-0412 to the buyer. Then the buyer cancels the order, and Al Noor does not supply the goods. Your app voids the invoice. Wafeq ASP makes a credit note for the full amount, with the reason DL8.61.1.A.
Example 3: the provider of the buyer rejects the invoice. The provider of the buyer finds an error in invoice INV-2026-0405. It rejects the invoice with the reason BV. Wafeq ASP withdraws the report of the invoice. Your app then submits a correction with the same number.
Three ways to change a document
Wafeq ASP does not let your app edit a document after it accepts it. If your app submits the same number again, Wafeq ASP refuses it. Use one of these ways instead:
| Situation | Use |
|---|---|
| You reduce an invoice, or you cancel a part of it. | A credit note. |
| You cancel all of an invoice that Wafeq ASP delivered to the buyer. | A void, or a credit note for the full amount. |
The provider of the buyer rejected a document with BV or SV. | A correction. |
Before you start
- Your app stores the
document_idof each accepted document. Refer to Submit a document. - Your app receives the
document.statusanddocument.reportedwebhook events. Refer to Track a document. - Your app submits documents with queued delivery, with the
Prefer: respond-asyncheader. Wafeq ASP accepts a correction only for a document that your app submitted in this way.
Issue a credit note: the four steps
A credit note reduces or cancels an invoice. Your app builds a credit note and submits it in the same way as an invoice. It uses POST /credit-notes/validate and POST /credit-notes.
| Step | Who | Action |
|---|---|---|
| 1 | Your app | Finds the original invoice. |
| 2 | Your app | Builds the request body. |
| 3 | Your app | Checks the credit note. |
| 4 | Your app | Submits the credit note and reads the response. |
Step 1: Find the original invoice
A credit note points at its original in one of three ways.
The original is an invoice in Wafeq ASP.
- Send
previous_invoice_source: "ap"and the document ID of the original inprevious_document_id. The valueapis the default. - Do not send
previous_invoice_numberorprevious_invoice_issue_date. Wafeq ASP takes them from the original. If your app sends them, Wafeq ASP refuses the request with422.
The original is an invoice that the taxpayer issued outside Wafeq ASP.
- Send
previous_invoice_source: "external"and the number of the original inprevious_invoice_number. - The issue date of the original, in
previous_invoice_issue_date, is optional. Use the formatYYYY-MM-DD. - Do not send
previous_document_id. - Keep the records of the original in your app. Wafeq ASP does not have them.
The credit note is a volume discount, with no original.
- Send
credit_note_reason_code: "VD". - Do not send
previous_document_id,previous_invoice_number,previous_invoice_issue_dateorprevious_invoice_source: "external". WithVD, Wafeq ASP refuses these fields with422.
Step 2: Build the request body
The body of a credit note has the same fields as the body of an invoice, with these differences:
credit_note_numberreplacesinvoice_number. This number must be different from the numbers of all other invoices and credit notes of the taxpayer.- No
due_dateand nopayment_means. A credit note does not have them. customeris the buyer of the original. If the original is in Wafeq ASP, send the samecustomer.idas on the original. If the original has nocustomer.id, do not send one.line_itemscontains the lines and the amounts to credit. Use positive quantities and prices.credit_note_reason_codegives the reason. If your app does not send it, Wafeq ASP usesDL8.61.1.A.
These are the reasons for a credit note in the UAE rules:
credit_note_reason_code | Reason |
|---|---|
DL8.61.1.A | Cancellation of the supply. |
DL8.61.1.B | A change in the nature of the supply, which changes its tax treatment. |
DL8.61.1.C | A change to the agreed price of the supply, for any reason, for example bad debt relief. |
DL8.61.1.D | A return of the goods or services, in full or in part, with a refund of the price, in full or in part. |
DL8.61.1.E | Tax that the seller charged in error, or a tax treatment that the seller applied in error. |
VD | Volume discount. The credit note does not point at an original. |
Wafeq ASP selects the type of the credit note:
- With
previous_document_id, the credit note takes the type of its original: 381 for a 380, and 81 for a 480. - Without
previous_document_id, Wafeq ASP selects the type from the lines, in the same way as for an invoice. Refer to Document types.
Example: a credit note for 2 returned chairs. The original invoice is in Wafeq ASP.
{
"credit_note_number": "CN-2026-0401",
"issue_date": "2026-10-20",
"source_id": "erp-credit-7701",
"previous_invoice_source": "ap",
"previous_document_id": "inv_0123456789ABCDEFGHIJ",
"credit_note_reason_code": "DL8.61.1.D",
"customer": {
"id": "0235:1999200000",
"name": "Gulf Fresh Foods LLC",
"vat_number": "199920000000003",
"legal_registration": { "type": "TL", "id": "CN-7654321", "authority": "Dubai Economy and Tourism" },
"address": { "street": "Warehouse 7, Al Quoz Industrial 3", "city": "Dubai", "country": "AE", "country_subentity": "DXB" }
},
"line_items": [
{ "description": "Office chairs, returned", "quantity": 2, "unit_price": 450, "tax_category": "S", "tax_percent": 5 }
]
}Example: a credit note for an invoice that the taxpayer issued outside Wafeq ASP.
{
"credit_note_number": "CN-2026-0402",
"issue_date": "2026-10-20",
"source_id": "erp-credit-7702",
"previous_invoice_source": "external",
"previous_invoice_number": "INV-2026-0411",
"previous_invoice_issue_date": "2026-09-28",
"credit_note_reason_code": "DL8.61.1.C",
"customer": {
"id": "0235:1999200000",
"name": "Gulf Fresh Foods LLC",
"vat_number": "199920000000003",
"legal_registration": { "type": "TL", "id": "CN-7654321", "authority": "Dubai Economy and Tourism" },
"address": { "street": "Warehouse 7, Al Quoz Industrial 3", "city": "Dubai", "country": "AE", "country_subentity": "DXB" }
},
"line_items": [
{ "description": "Price reduction on office desks", "quantity": 1, "unit_price": 300, "tax_category": "S", "tax_percent": 5 }
]
}Step 3: Check the credit note
Send the request body to POST /credit-notes/validate, with the headers of the taxpayer. Wafeq ASP runs all the checks of a submission. It keeps nothing, delivers nothing and reports nothing.
For an original in Wafeq ASP, Wafeq ASP reads the original. These errors are about the original:
| Error code | Meaning |
|---|---|
previous_invoice_not_found | The taxpayer has no document with this document ID. |
invalid_previous_document | The original is not an invoice that the taxpayer issued, or it has no number or no issue date. |
previous_invoice_party_mismatch | customer.id is different from the original. |
previous_invoice_lookup_failed | Wafeq ASP could not read the original at this time. Check the credit note again later. |
Step 4: Submit the credit note and read the response
Send the same body to POST /credit-notes. Send the same headers as for an invoice, with Idempotency-Key and Prefer: respond-async.
Wafeq ASP delivers the credit note to the buyer and reports it to the FTA, in the same way as an invoice. The responses are the same as for an invoice. Refer to Submit a document.
The credit note gets its own document_id. When the credit note points at its original with previous_document_id, the original shows the number of the credit note in credited_by_invoice_number.
Void an invoice: the three steps
A void cancels all of an invoice. Wafeq ASP makes a credit note from the stored invoice and submits it. Your app sends no lines.
Use a void only for an invoice that Wafeq ASP delivered to the buyer. Do not use a void for the invoices below. Issue a credit note for the full amount instead:
- An invoice that Wafeq ASP reported to the FTA only.
- A self-billed invoice (389). For this invoice, use
POST /self-billing/credit-notes.
| Step | Who | Action |
|---|---|---|
| 1 | Your app | Checks the invoice. |
| 2 | Your app | Sends the void request. |
| 3 | Your app | Reads the response. |
Step 1: Check the invoice
Read the invoice with GET /invoices/{document_id}. Wafeq ASP refuses the void in these cases:
| Response | Cause |
|---|---|
404 | The taxpayer has no invoice with this document ID. |
400 | The document is a credit note, or the taxpayer did not issue it. |
409, code delivery_unsettled | The delivery of the invoice is not finished, or it has a problem that Wafeq ASP handles. For example, the provider of the buyer rejected the invoice. |
409, with the number of a credit note | A credit note already points at the invoice. credited_by_invoice_number shows the number of this credit note. |
Step 2: Send the void request
Send POST /invoices/{document_id}/void, with the headers of the taxpayer. The body is optional. It has two fields:
reason: a free text. Wafeq ASP adds it to the notes of the credit note, afterVoid:.credit_note_reason_code: the reason code. If your app does not send it, Wafeq ASP usesDL8.61.1.A(cancellation of the supply).
curl https://api.wafeq.com/ae/v1/invoices/inv_0123456789ABCDEFGHIJ/void \
-H "Authorization: Api-Key $API_KEY" \
-H "x-ae-taxpayer-id: $TAXPAYER_ID" \
-H "x-ae-grant-id: $GRANT_ID" \
-H "Content-Type: application/json" \
-d '{"reason": "The buyer cancelled the order", "credit_note_reason_code": "DL8.61.1.A"}'Wafeq ASP then does these actions:
- It makes a credit note from the stored request of the invoice. The credit note has the same buyer and the same lines. Thus it credits the full amount.
- It gives the credit note the type 381 for a 380, or 81 for a 480.
- It gives the credit note a number. Your app does not choose this number.
- It delivers the credit note to the buyer and reports it to the FTA. The call waits until the delivery ends.
Step 3: Read the response
The response has the same fields as the response of a submission without Prefer: respond-async:
invoice_id: the number that Wafeq ASP gave the credit note.document_id: the document ID of the credit note. Store it.status: read it in the same way as for a submission. Refer to Submit a document.
Submit a correction: the five steps
Sometimes the provider of the buyer rejects a document with one of these reasons:
BV: the document breaks a business rule.SV: the format of the document is not correct.
Then Wafeq ASP withdraws the report of the document. Your app then submits a correction: a new version of the document, with the same number.
Wafeq ASP accepts a correction only in these conditions:
- The provider of the buyer rejected the original with
BV,SVor both. - Your app submitted the original with
Prefer: respond-async. - The original is a Live document. Sandbox does not accept corrections.
Wafeq ASP accepts one correction for each rejected document. If the provider of the buyer rejects the correction too, the next correction points at the rejected correction.
| Step | Who | Action |
|---|---|---|
| 1 | Wafeq ASP | Withdraws the report of the original. |
| 2 | Your app | Corrects the document. |
| 3 | Your app | Checks the correction. |
| 4 | Your app | Submits the correction. |
| 5 | Your app | Reads the response. |
Step 1: Wafeq ASP withdraws the report of the original
Your app does nothing in this step. Wafeq ASP starts it automatically.
- Your app gets a
document.statusevent for the original. The event hasdelivery_status: rejected, andreason_codeisBVorSV. - Wafeq ASP waits for the answer of the FTA to the report of the original.
- If the FTA accepted the report, Wafeq ASP sends a withdrawal. When the FTA accepts the withdrawal,
tdd_statusof the original becomeswithdrawn. Wafeq ASP sends adocument.reportedevent withtax_authority_status: WITHDRAWN. - If the FTA rejected the report, Wafeq ASP sends no withdrawal.
GET /invoices/{document_id} of the original shows the progress of this step in reporting_recovery.
Step 2: Correct the document
Show the rejection to your user. delivery_reason_text of the original gives the text from the provider of the buyer, if the provider sent one.
Build the body of the correction:
- Use the same endpoint as for the original. For example, use
POST /invoicesfor an invoice. - Use the same number as the original.
- Keep the same document type. For example, a 380 cannot become a 480.
- Send the full body with the corrected content, not only the changes.
- Add
corrects_document_id, with the document ID of the original.
Example: the correction of invoice INV-2026-0405.
{
"invoice_number": "INV-2026-0405",
"corrects_document_id": "inv_KLMNOPQRST0123456789",
"issue_date": "2026-10-15",
"due_date": "2026-11-14",
"source_id": "erp-invoice-9105",
"customer": {
"id": "0235:1999200000",
"name": "Gulf Fresh Foods LLC",
"vat_number": "199920000000003",
"legal_registration": { "type": "TL", "id": "CN-7654321", "authority": "Dubai Economy and Tourism" },
"address": { "street": "Warehouse 7, Al Quoz Industrial 3", "city": "Dubai", "country": "AE", "country_subentity": "DXB" }
},
"line_items": [
{ "description": "Office desks", "quantity": 4, "unit_price": 1200, "tax_category": "S", "tax_percent": 5 }
]
}Step 3: Check the correction
Send the body to the check endpoint of the original, for example POST /invoices/validate. Wafeq ASP checks that the original can get a correction. The check does not refuse the number of the original, and it starts nothing.
If the original cannot get a correction, the response has an error. The check does not wait for step 1. Thus a correct check does not mean that step 1 is complete.
Step 4: Submit the correction
Send the same body to the same endpoint as the original. Add a new Idempotency-Key. Do not use the key of the original.
curl https://api.wafeq.com/ae/v1/invoices \
-H "Authorization: Api-Key $API_KEY" \
-H "x-ae-taxpayer-id: $TAXPAYER_ID" \
-H "x-ae-grant-id: $GRANT_ID" \
-H "Idempotency-Key: erp-invoice-9105-fix-1" \
-H "Content-Type: application/json" \
-d @correction.jsonWafeq ASP always uses queued delivery for a correction. Your app does not need to send Prefer: respond-async.
Step 5: Read the response
202, status: queued
- Meaning: Wafeq ASP accepted the correction. The correction has a new
document_id, and the same number as the original. - Next: store the new
document_id, and track the correction as any other document. Wafeq ASP also sends adocument.statusevent for the original. This event hasreason_code: replacement_queued, and the new document ID inreplacement_document_id.
409, code correction_not_ready
- Meaning: step 1 is not complete. Wafeq ASP accepted nothing. The response contains
reporting_recovery, with the progress of step 1. - Next: wait. Then send the same request again, with the same
Idempotency-Key.
Other refusals:
| Response | Cause |
|---|---|
422, code idempotency_key_required | The request has no Idempotency-Key. |
422, code correction_number_or_type_mismatch | The number or the document type is different from the original. |
409, code c3_validation_rejection_required | The provider of the buyer did not reject the original with BV or SV. |
404, code queued_document_not_found | The original does not exist, or your app did not submit it with Prefer: respond-async. |
When the FTA rejects a report
This section is not a step. It tells you what a rejection by the FTA means.
tdd_statusof the document isrejected.- The
document.reportedevent hastax_authority_status: NOT_COMPLIANT. - This means that the FTA did not accept a report of the document. This can be the report of the document, or a withdrawal.
- The delivery to the buyer has its own status. A rejection by the FTA does not change it.
Corrections in Sandbox
- Wafeq ASP refuses a correction with
403and the codesandbox_reporting_disabled. A check withcorrects_document_idgives an error with the same code. - Sandbox reports nothing to the FTA. Thus Wafeq ASP withdraws no report in Sandbox.
Next
Stage 6: Go live.
Updated about 14 hours ago