1. 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 example inv_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:

SituationUse
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_id of each accepted document. Refer to Submit a document.
  • Your app receives the document.status and document.reported webhook events. Refer to Track a document.
  • Your app submits documents with queued delivery, with the Prefer: respond-async header. 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.

StepWhoAction
1Your appFinds the original invoice.
2Your appBuilds the request body.
3Your appChecks the credit note.
4Your appSubmits 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 in previous_document_id. The value ap is the default.
  • Do not send previous_invoice_number or previous_invoice_issue_date. Wafeq ASP takes them from the original. If your app sends them, Wafeq ASP refuses the request with 422.

The original is an invoice that the taxpayer issued outside Wafeq ASP.

  • Send previous_invoice_source: "external" and the number of the original in previous_invoice_number.
  • The issue date of the original, in previous_invoice_issue_date, is optional. Use the format YYYY-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_date or previous_invoice_source: "external". With VD, Wafeq ASP refuses these fields with 422.

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_number replaces invoice_number. This number must be different from the numbers of all other invoices and credit notes of the taxpayer.
  • No due_date and no payment_means. A credit note does not have them.
  • customer is the buyer of the original. If the original is in Wafeq ASP, send the same customer.id as on the original. If the original has no customer.id, do not send one.
  • line_items contains the lines and the amounts to credit. Use positive quantities and prices.
  • credit_note_reason_code gives the reason. If your app does not send it, Wafeq ASP uses DL8.61.1.A.

These are the reasons for a credit note in the UAE rules:

credit_note_reason_codeReason
DL8.61.1.ACancellation of the supply.
DL8.61.1.BA change in the nature of the supply, which changes its tax treatment.
DL8.61.1.CA change to the agreed price of the supply, for any reason, for example bad debt relief.
DL8.61.1.DA 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.ETax that the seller charged in error, or a tax treatment that the seller applied in error.
VDVolume 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 codeMeaning
previous_invoice_not_foundThe taxpayer has no document with this document ID.
invalid_previous_documentThe original is not an invoice that the taxpayer issued, or it has no number or no issue date.
previous_invoice_party_mismatchcustomer.id is different from the original.
previous_invoice_lookup_failedWafeq 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.
StepWhoAction
1Your appChecks the invoice.
2Your appSends the void request.
3Your appReads the response.

Step 1: Check the invoice

Read the invoice with GET /invoices/{document_id}. Wafeq ASP refuses the void in these cases:

ResponseCause
404The taxpayer has no invoice with this document ID.
400The document is a credit note, or the taxpayer did not issue it.
409, code delivery_unsettledThe 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 noteA 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, after Void: .
  • credit_note_reason_code: the reason code. If your app does not send it, Wafeq ASP uses DL8.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:

  1. 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.
  2. It gives the credit note the type 381 for a 380, or 81 for a 480.
  3. It gives the credit note a number. Your app does not choose this number.
  4. 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, SV or 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.

StepWhoAction
1Wafeq ASPWithdraws the report of the original.
2Your appCorrects the document.
3Your appChecks the correction.
4Your appSubmits the correction.
5Your appReads the response.

Step 1: Wafeq ASP withdraws the report of the original

Your app does nothing in this step. Wafeq ASP starts it automatically.

  1. Your app gets a document.status event for the original. The event has delivery_status: rejected, and reason_code is BV or SV.
  2. Wafeq ASP waits for the answer of the FTA to the report of the original.
  3. If the FTA accepted the report, Wafeq ASP sends a withdrawal. When the FTA accepts the withdrawal, tdd_status of the original becomes withdrawn. Wafeq ASP sends a document.reported event with tax_authority_status: WITHDRAWN.
  4. 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 /invoices for 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.json

Wafeq 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 a document.status event for the original. This event has reason_code: replacement_queued, and the new document ID in replacement_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:

ResponseCause
422, code idempotency_key_requiredThe request has no Idempotency-Key.
422, code correction_number_or_type_mismatchThe number or the document type is different from the original.
409, code c3_validation_rejection_requiredThe provider of the buyer did not reject the original with BV or SV.
404, code queued_document_not_foundThe 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_status of the document is rejected.
  • The document.reported event has tax_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 403 and the code sandbox_reporting_disabled. A check with corrects_document_id gives 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.