When you connect your system to the Fatoora platform through APIs, the journey does not end when you send the invoice. The most technically important part is reading the API response: what did the Authority say about your invoice? Was it accepted? Accepted with a warning? Or rejected? This guide presents ready, annotated samples for every response case, so you can build the logic for handling them in your system with confidence.
This reference is aimed at development teams integrating with Qoyod e-invoicing or building their own integration with the Fatoora platform of the Zakat, Tax and Customs Authority (ZATCA). We explain the response structure field by field, and present three complete samples: an accepted invoice (CLEARED), an accepted invoice with a warning (CLEARED with WARNING), and a rejected invoice (ERROR with code 422). Each sample is explained in English with the meaning of every field.
What is an API response?
When your system sends an invoice to the Fatoora platform, the platform replies with a structured message in JSON format. This message is the “Response.” The response carries three key pieces of information: the invoice status, the detailed validation results, and the cleared copy of the invoice if it was accepted.
A fundamental difference between two APIs determines the shape of the response. Business invoices (B2B) go through the Clearance API, where the invoice is cleared in real time before it is handed to the buyer. Consumer invoices (B2C), on the other hand, go through the Reporting API, where the Authority is reported to within 24 hours after the invoice is issued to the buyer. The response in both cases shares almost the same structure, with a slight difference we will clarify.
This guide focuses on reading and understanding the response. If you are looking for how to build the request itself, the dedicated reference for that is separate. And if you are looking for how to handle error codes and the various failure cases, see the Error Handling in the API guide, which complements this one.
Response structure: the core fields
Before presenting the samples, here are the fields that recur in every response. Understanding them makes reading the samples later much easier.
- status: the overall status of the invoice. Its common values:
CLEARED(cleared),REPORTED(reported),NOT_CLEARED(not cleared),ERROR(rejected due to a structural error). - validationResults: an object holding the detailed validation results. Inside it are three lists:
infoMessages(informational messages),warningMessages(warnings that do not prevent clearance), anderrorMessages(errors that prevent clearance). - clearedInvoice: the cleared copy of the invoice in Base64 format (signed XML). It appears only on successful clearance through the Clearance API.
- reportingStatus or clearanceStatus: the final status of the operation depending on the API (reporting or clearance).
The practical rule: always start by reading status, then go into validationResults to understand the details, and finally extract clearedInvoice if the operation succeeded. This order builds clear logic in your system.
Understanding the three lists inside validationResults
The real heart of any response is the validationResultsobject. Inside it are three separate lists, each with a completely different role. Confusing them is the most common mistake developers new to the integration make.
- infoMessages (informational messages): positive or neutral messages telling you that a certain validation rule passed. Its most famous example is confirming the invoice’s compliance with the UBL 2.1 standard. The presence of messages here does not mean there is a problem; it means the platform checked this aspect and found it sound. Do not build rejection logic on them.
- warningMessages (warnings): notes that do not prevent clearance but deserve review. The invoice passes, but the platform alerts you to something that could become a problem later. Treat them as a deferred to-do list, not a barrier.
- errorMessages (blocking errors): this is the decisive list. Any item in it means the invoice is rejected. If it is not empty, the invoice was not cleared no matter how the rest of the fields look. Make checking this list the first step in your handler.
Each item in these lists carries the same structure: type (message type), code (rule code), category (rule category such as KSA or EN16931), message (text description), andstatus (the status of this specific rule). The code code is your key to building an internal table that translates each code into an English message understandable to your user.
Validation rule categories (category)
The category field tells you where the applied rule came from. Knowing the category helps you understand the nature of the error quickly:
- XSD validation: a check on the structure of the XML file itself and its conformance to the schema. Errors in this category are purely structural, such as a missing field or a wrong element order.
- EN16931: rules of the European e-invoicing standard on which the Authority’s specifications are built. Most calculation and logic rules belong to this category.
- KSA: rules specific to the Kingdom added by the Authority on top of the base standard, such as local tax number requirements or national address fields.
When building your internal translation table, classify the codes by their category. This speeds up diagnosing any future error: an XSD error means a problem in generating the XML, an EN16931 error usually means a calculation problem, and a KSA error means a missing mandatory local field.
CLEARED: cleared successfully
CLEARED with WARNING: cleared with notes
ERROR (422): rejected
The fields: validationResults and clearedInvoice
HTTP status codes in Fatoora platform responses
Before reading the response body, look at the HTTP status code in the header. This code tells you the overall result before you analyze the details.
- 200 OK: the operation succeeded and the invoice was cleared or reported. Read the response body to extract the cleared copy.
- 202 Accepted: the operation was accepted but with warnings. The invoice is cleared, but review the
warningMessageslist to correct what is needed in upcoming invoices. - 400 Bad Request: an error in the format of the request itself before it reached the validation logic.
- 401 Unauthorized: an authentication problem. Check your certificate or your access token.
- 422 Unprocessable Entity: the request is structurally correct but failed substantive validation. This is the most common case that needs programmatic handling. You will find the details in
errorMessages. - 500 Internal Server Error: an error in the platform service. Retry later.
Code 422 is the focus of this guide in the rejection case, because it means your invoice arrived formally sound but its content did not pass the validation rules. Do not treat it as a network error; read its messages and correct the invoice.
Sample one: response of a cleared invoice (CLEARED)
This is the ideal case. You sent a business (B2B) invoice through the Clearance API, it passed all validation rules, so the platform returned the invoice cleared and signed. The HTTP code here is 200.
Field explanation:
- validationResults.status = “PASS”: all validation rules passed. This is the most important sign of complete success.
- infoMessages: a single informational message confirming the invoice’s compliance with the UBL 2.1 standard and the Authority’s specifications. This is not an error nor a warning, but a positive confirmation.
- warningMessages = []: an empty list, i.e. no warnings. Excellent.
- errorMessages = []: an empty list, i.e. no errors. The invoice is completely clean.
- clearedInvoice: this is the golden output. A Base64 string containing the signed XML file of the cleared invoice. Decode it to get the official copy that carries the cryptographic stamp and the QR code.
- clearanceStatus = “CLEARED”: a final confirmation that the invoice was cleared by the Authority, and you can now hand it to the buyer.
Handling logic in your system: if clearanceStatus = "CLEARED" anderrorMessages are empty, save clearedInvoice as the official copy, mark the invoice in your database as cleared, and send it to the buyer.
Note that the Reporting API response for consumer (B2C) invoices resembles this sample, but instead of clearedInvoice andclearanceStatus you find the field reportingStatus = "REPORTED". The reason is that consumer invoices are reported after they are issued, not cleared before, so the platform does not return a new cleared copy.
How to read the cleared copy (clearedInvoice)
The clearedInvoice is not merely a confirmation; it is the actual product of the operation. Its value is a long string encoded in Base64. When you decode it you get a complete XML file to the UBL 2.1 standard, but it differs from the invoice you sent in one fundamental matter: the platform added to it the official clearance elements.
The elements the platform adds to the cleared copy:
- Cryptographic Stamp: a digital signature proving the invoice passed through the Authority and was cleared. It cannot be forged or modified after clearance.
- Unique Identifier (UUID): a unique identification number for this specific invoice, ensuring it is not duplicated.
- QR Code: a code containing the invoice data and its signature, printed on the buyer’s copy.
- Previous Invoice Hash: a value linking this invoice to the one before it to form an unbreakable integrity chain.
The golden rule: the official cleared copy is what clearedInvoicereturns, not the invoice you sent. Store this decoded copy as an XML file in your system, and treat it as the reference in any review by the Authority. Keeping only the sent invoice without the cleared copy is a common mistake that causes problems during review.
Sample two: cleared response with a warning (CLEARED with WARNING)
Here the invoice was cleared and succeeded, but the platform detected something that deserves your attention. The HTTP code here is usually 202. The warning does not prevent clearance, but it is a sign to correct something before it turns into an error in the future, or to alert you to a detail that may be inaccurate.
Field explanation:
- validationResults.status = “WARNING”: the overall status is warning, not error. The important distinction: the invoice was cleared despite the warning.
- warningMessages: a list with a single warning. The code
BR-KSA-F-06and the message clarify that the buyer identifier (BT-46) is recommended but missing. This is allowed but not ideal. - errorMessages = []: no blocking errors. That is why the invoice was cleared.
- clearedInvoice: present. The cleared invoice came back complete despite the warning.
- clearanceStatus = “CLEARED”: actually cleared.
The fundamental difference between WARNING and ERROR: the warning does not prevent clearance, so the invoice is valid. The error prevents clearance, so the invoice is rejected. Therefore your system’s logic must distinguish between them. When a warning appears, clear the invoice but log the warning in an internal record and review it periodically to correct its source in upcoming invoices.
Common examples of warnings: a recommended field missing, an accepted but non-preferred date format, or a missing optional detail. Do not ignore warnings entirely, as some may turn into a blocking error when the Authority’s rules are updated later.
| Criterion | Warning (WARNING) | Error (ERROR 422) |
|---|---|---|
| Result | The invoice is cleared | Rejected |
| The field | warningMessages | errorMessages |
| Action | Review later | Correct and resend |
Sample three: response of a rejected invoice (ERROR with code 422)
This case needs the greatest amount of programmatic handling. The invoice arrived structurally sound, but it did not pass the substantive validation rules, so the platform rejected it. The HTTP code here is 422. The invoice was not cleared, and there is no clearedInvoice.
Field explanation:
- validationResults.status = “ERROR”: the overall status is error. The invoice is rejected.
- errorMessages: a list with two errors. The first
BR-KSA-EN16931-08: the tax amount in the tax category does not match the calculated value. The secondBR-CO-15: the invoice total including tax does not equal the sum of the line items plus tax. Both are calculation errors in the invoice. - infoMessages andwarningMessages both empty: because validation stopped at the blocking errors.
- clearedInvoice = null: there is no cleared copy, because the invoice was not cleared in the first place.
- clearanceStatus = “NOT_CLEARED”: an explicit confirmation that the invoice was not cleared.
Handling logic in your system: on code 422 andstatus = "ERROR", do not hand the invoice to the buyer. Read every item in errorMessages, show the user a clear message explaining each error in English, correct the invoice, then resend. Each error code has a specific meaning that helps you direct the correction precisely.
The errors in the sample above are calculation errors, and they are the most common. Their cause is usually incorrect rounding of fractions, a mismatch of the tax rate (15%) with the calculated amount, or an error in summing the line items. Your system’s handling of these errors starts with tuning the calculation logic before sending in the first place.
For a deeper explanation of each category of error codes and how to build retry and logging logic, see the Error Handling in the APIguide. This guide presents the samples; that one presents the complete handling strategy.
Read status
CLEARED: save and deliver
WARNING: clear and log
ERROR: correct and resend
The difference between the clearance response and the reporting response
Since the clearance and reporting APIs differ in the nature of their work, their responses differ in two important details. This table summarizes the difference:
| Aspect | Clearance B2B | Reporting B2C |
|---|---|---|
| Operation timing | Before delivery to the buyer (real time) | After issuing it to the buyer (within 24 hours) |
| Status field | clearanceStatus |
reportingStatus |
| Success value | CLEARED |
REPORTED |
| Cleared copy returned | Yes (clearedInvoice) |
No (the invoice was already issued) |
| validationResults structure | Identical | Identical |
Bottom line: one validationResults reading logic for both APIs. The only difference is in the name of the final status field, and in whether the cleared copy is present. Build your handler to check both fields clearanceStatus andreportingStatus together so it works with both APIs.
Common error codes and how to translate them for your user
When a rejected invoice appears with code 422, the error code (code) is your guide to the correction. Building an internal table that links each code to a clear English message turns your user’s experience from frustration into practical guidance. Here are the most prominent categories you will encounter:
- Calculation errors (EN16931): a mismatch of the calculated tax amount with the declared one, or a difference of the invoice total from the sum of line items plus tax. The cause is usually rounding of fractions or the order of calculation operations. The fix: unify your rounding logic before sending, and calculate the tax at the line-item level then sum.
- Tax number errors (KSA): a missing tax number or one in an incorrect format for the seller or buyer in business invoices. The fix: verify the length and validity of the tax number before building the invoice.
- Structural errors (XSD validation): a mandatory field missing in the XML file or a wrong element order. The fix: review your XML generator so it matches the UBL 2.1 schema precisely.
- Timing and sequence errors: an illogical issue date or a break in the hash chain between invoices. The fix: ensure every invoice carries the hash of the previous invoice in the correct order.
The governing rule: do not show the raw error code to the end user. Translate it into an English sentence that tells them what happened and what the action is. Example: instead of showing BR-CO-15, show “The invoice total does not match the sum of line items with tax, review the amounts.” This is the difference between a system that confuses the user and one that helps them.
The invoice lifecycle from request to response
To understand the response fully, imagine the path the invoice travels. This sequence clarifies where the response falls in the big picture:
- Creation: your system creates the invoice in XML format to the UBL 2.1 standard, and adds the mandatory fields and the initial stamp.
- Sending: your system sends the request to the Fatoora platform through the Clearance API (B2B) or the Reporting API (B2C).
- Validation: the platform checks the invoice against the XSD, EN16931, and KSA rules, and builds the three message lists.
- Response: the platform returns the JSON that contains
statusandvalidationResultsandclearedInvoice(on clearance). - Processing: your system reads the response, and decides: clear and save, or log a warning, or reject and correct.
The response, then, is the closing loop in this cycle. Reading it correctly determines what happens in the next step: is the invoice delivered to the buyer, or does it return to the correction table? Here lies the importance of building precise handling logic instead of merely checking the HTTP code.
If you want to go deeper into building the request and the clearance and reporting APIs, the two guides Clearance API andReporting API explain both ends of the operation in detail, while this guide focuses on what comes back to you after sending.
Best practices in reading the response
Reading the response properly protects you from silent errors that may cause tax problems later. Here are practical practices:
- Check errorMessages first: if it is not empty, the invoice is rejected no matter what the rest of the fields are. Do not settle for reading the overall
status. - Log every full response: save the complete JSON text in your system’s log. In any dispute or review, this log is your proof.
- Decode clearedInvoice and store it: the cleared copy is the official reference, not the invoice you sent. Save the signed XML.
- Handle warnings periodically: do not halt operations at a warning, but gather the warnings and review them weekly to correct their source.
- Retry intelligently: code 500 is retried later, whereas 422 is not retried until the invoice is actually corrected. Resending a wrong invoice as is will not succeed.
How to build a reliable response handler
A good response handler follows a fixed sequence that does not change from one invoice to another. This sequence ensures you will not deliver a rejected invoice by mistake, nor reject a cleared invoice. Here is the logic step by step in practical terms:
- Step one: check the HTTP code. If it is 500 or 503, the problem is in the service not in your invoice, so retry later within a defined retry policy. If it is 401, stop the operation and check authentication.
- Step two: check errorMessages. If it is not empty, the invoice is rejected. Gather all its messages, translate them to English, show them to the user, and mark the invoice as needing correction. Do not proceed a single step further.
- Step three: check the clearance status. Read
clearanceStatusorreportingStatus. The valueCLEAREDorREPORTEDmeans success. The valueNOT_CLEAREDmeans rejection even if no explicit error messages appear. - Step four: handle the warnings. If there are
warningMessageswith a successful clearance, clear the invoice and log the warnings in a separate record for periodic review. Do not halt the operation because of them. - Step five: extract the cleared copy. If
clearedInvoiceis present, decode the Base64, save the signed XML file, and link it to the invoice record in your database. - Step six: log everything. Save the complete JSON text of the response with its timestamp. This log is your reference in any later review.
Committing to this sequence makes your handler predictable in behavior and testable. Test it against the three samples in this guide: pass it a CLEARED response and it saves the copy, a WARNING response and it clears and logs, and a 422 response and it rejects and corrects. If it behaves correctly in the three cases, your logic is sound.
Common mistakes in building the handler
These are mistakes that recur among development teams new to the integration; avoid them from the start:
- Settling for the HTTP code: a 200 code does not necessarily mean a clean invoice, as it may carry warnings. Always check the response body.
- Ignoring warnings entirely: some warnings turn into blocking errors when the Authority’s rules are updated. Review them periodically.
- Saving the sent invoice instead of the cleared one: the official reference is
clearedInvoice, not what you sent. - Resending a 422 invoice without correction: it will not succeed. Code 422 means a substantive error that must be fixed first.
- Showing the raw English error message: the Arabic-speaking user needs clear guidance in their language, not a technical code.
How Qoyod helps you handle Fatoora platform responses
If you do not want to build the response-reading and error-parsing logic manually, Qoyod handles this entire layer on your behalf. When you use Qoyod e-invoicing:
- Real-time clearance with the Fatoora platform: Qoyod sends business (B2B) invoices for clearance in real time, reports consumer (B2C) invoices within 24 hours, and reads and interprets the response automatically without any code on your side.
- Automatic cryptographic stamp (CSID) management: Qoyod manages the cryptographic stamp identifier certificate to comply with the Authority’s requirements, so you do not need to deal with certificates manually.
- Clear error messages in Arabic: when the platform rejects an invoice with code 422, Qoyod translates the error message and shows an understandable reason that guides you to the correction directly.
- Saving the cleared copy and the hash chain: Qoyod stores the cleared copy (clearedInvoice) and the invoice hash chain to verify the integrity of the sequence in any review.
This way you get a complete API integration result without building the handling layer yourself, and you focus on your business instead of maintaining code.
Frequently asked questions
What is the difference between CLEARED and REPORTED in the response?
CLEARED means a business (B2B) invoice was cleared in real time through the Clearance API before being handed to the buyer. REPORTED means a consumer (B2C) invoice was reported to the Authority after it was issued through the Reporting API. The first returns a cleared copy, and the second does not.
What do I do if the response is WARNING?
The invoice is cleared and you can hand it over. Log the warning in your system and review its source periodically to correct upcoming invoices. The warning does not prevent clearance, but ignoring it permanently may turn it into a blocking error when the Authority’s rules are updated.
Why do I get code 422 even though my invoice looks correct?
Code 422 means the failure of substantive, not formal, validation. The common causes are calculation errors: incorrect rounding, a mismatch of the 15% tax rate with the amount, or an error in summing the line items. Read errorMessages to know the error precisely.
Where do I find the cleared copy of the invoice?
In the clearedInvoice field inside the response, and it is a string in Base64 format. Decode it to get the signed XML file that carries the cryptographic stamp and the QR code. This is the official copy cleared by the Authority.
Should I save the full text of the response?
Yes. Save the complete JSON text of every response in your system’s log. In any review or tax dispute, this log is your proof of what the platform returned and of the final status of the invoice.
How do I programmatically distinguish an accepted invoice from a rejected one?
Check errorMessages first: if it is not empty the invoice is rejected. Then check clearanceStatus or reportingStatus: the value CLEARED or REPORTED means success, andNOT_CLEARED means rejection. Do not settle for reading the HTTP code alone.
Let Qoyod handle the integration with the Fatoora platform
Instead of building the response-reading and error-parsing logic manually, issue invoices cleared by the Authority directly from Qoyod. Real-time clearance, automatic stamp management, and clear error messages in Arabic.