This guide is aimed at developers and technical integration teams who actually build the reporting flow on top of the Fatoora platform within Qoyod e-invoicing. It has one specific goal: to put in your hands the single reporting request for the simplified tax invoice (B2C) in full detail. The endpoint, the method, the headers, the request body, the response shape, the 24-hour window, and how to handle every state the platform returns.
We do not re-explain here what reporting is as a regulatory concept. That is a separate story we covered in the guide Reporting in e-invoicing. Here we assume you are a developer writing the code, and you want to know exactly which endpoint to call, which header to send, which JSON fields to put in the body, and which shape to expect in return. If you want the full catalog of every endpoint in one place, refer to the guide Fatoora platform API endpoints, and this guide zooms the lens onto one of them: the single reporting endpoint.
Reporting is the path taken by the simplified tax invoice addressed to the final consumer (B2C). You issue the invoice, hand it to the buyer immediately at the point of sale, then notify the authority about it within a later window. This differs fundamentally from the Clearance path, which concerns B2B invoices between businesses, where you wait for the authority’s approval before handing over the invoice. We explain this difference in detail in the last section, and you can refer to the guide Clearance in e-invoicing to grasp the opposite path.
When do you call the reporting endpoint?
You call the reporting endpoint after you have issued the simplified invoice and handed it to the buyer, not before. This order is essential. In the B2C path you do not wait for the authority’s response to hand over the invoice; you deliver it instantly with the QR code printed on it, then send a copy of it to the authority afterward.
The platform grants you a window of twenty-four hours from the moment the invoice is issued to complete the reporting. This window is legal, not merely technical. Missing it counts as a compliance violation, even if the invoice itself is valid. So design your system to notify the authority as soon as possible, not on the edge of the deadline.
In practice, the moment of issuance is separated from the moment of reporting. This separation is a feature, not a flaw. It means the point of sale completes the payment and prints the receipt without waiting for a network response, then a background component takes over uploading the invoices batch by batch. Design this component to be non-blocking for the cashier experience, resilient to network outages, and capable of retrying.
The difference between issuance and reporting in your system design
In your architecture, separate three responsibilities. The first is generating the invoice, signing it locally, and printing it. The second is saving the invoice to a persistent reporting queue. The third is calling the reporting endpoint and processing its response. Separating these layers makes your system withstand network fluctuations without disrupting the sale.
The simplified B2C invoice is signed locally with the Cryptographic Stamp Identifier (CSID) bound to the device, and it does not need the authority’s signature to become valid. This is the opposite of the B2B invoice, which is not handed over until it is stamped by the authority. Understand this difference well before you build, because it determines when you print the receipt and when you wait.
General principles before any reporting request
Before you call the reporting endpoint, fix in your mind principles that apply to all of the platform’s interfaces. Ignoring one of them means the request is rejected at the gateway level before it even reaches the business logic, so you spend time tracing an error whose source is in the envelope, not in the invoice.
First, the request follows the REST pattern over HTTPS, and exchanges its metadata in JSON format. The invoice itself is carried in XML compliant with UBL 2.1, but it is sent Base64-encoded inside a JSON field named invoice. Any request that does not comply with this envelope is rejected outright without inspecting the invoice content.
Second, the reporting endpoint is an operational endpoint called with every simplified invoice, not once at setup. This distinguishes it from the compliance and onboarding interfaces that are called once per device. The operational endpoint must be fast and non-blocking for the user experience, especially at high-frequency points of sale that issue dozens of invoices per minute.
Third, the two environments are completely separate. There is a test environment (Sandbox) for compliance and development, and a production environment (Production) for real invoices. Each environment has a different base URL and a different Cryptographic Stamp Identifier. Real reporting runs on the Production CSID, while checking your system’s conformance runs on the compliance identifier in the Sandbox. Do not mix the two.
Fourth, always read the full response. The transport-level status code alone is not enough, as the platform may return a success code at the transport level while the response body carries a failed validation result. The actual result lives in the field validationResults, so make reading it a mandatory part of your logic, not an optional step.
The endpoint and method
The single reporting endpoint is one relative path, called with the method POST over HTTPS. The relative path is appended after the base URL of the environment you are working on, and you do not hardcode the base URL directly in the code.
Note that the same relative path is called under both base URLs. The difference between test and production is the base URL and the identifier used, not the path. Fix the base URL in a single environment variable, and change it when you move to production. Mixing the two environments is a common cause of rejected requests.
Is there a batch reporting endpoint?
This page focuses on single reporting for one invoice, which is the most widely used pattern at points of sale. Single reporting sends one invoice per request, so you read its result clearly and retry it in isolation from the others. This is clearer for error tracing than sending one large batch whose results are mixed together.
The required headers
The reporting request carries four essential headers. Each one has a specific role, and neglecting any of them may bounce the request at the gateway level before it reaches the business logic.
Authorization: authentication of typeBasic. The value is the Production CSID and its associated password, combined and Base64-encoded. This is what proves to the authority the identity of the device that is reporting.Accept-Version: its value isV2. It pins the interface version, protecting your integration from unexpected changes when the platform is upgraded.Clearance-Status: its value is0in the reporting path. This value is what distinguishes a reporting request from a clearance request, whose value is1.Content-Type: its value isapplication/json. The request body is always JSON, even if it carries an encoded XML document inside it.
The header Authorization deserves special attention. The value is not a human username and password, but the Production CSID as the username, and its associated secret as the password. You obtain this identifier after completing the compliance stage and obtaining the production certificate. For detail, refer to the guide Compliance CSID certificate.
A note on naming: the correct abbreviation is CSID , meaning the Cryptographic Stamp Identifier, and not any other ordering of the letters. Writing it wrong in the code or documentation causes confusion in integration teams, so get it right from the start.
The header Accept-Language is optional and concerns the language of error messages only, not the invoice content. The invoice content itself in UBL 2.1 requires mandatory Arabic by the authority’s requirements. Do not mix the two, or you will see language warnings unrelated to the interface messages.
The request body
The reporting request body is a JSON that carries three essential fields. These three fields are the backbone of every operational request, whether in reporting or clearance.
invoiceHash: the SHA-256 hash of the document, Base64-encoded. It binds the request to the invoice content, exposing any tampering. For detail, refer to the guide Hashing in e-invoicing.uuid: the unique identifier of the invoice, a non-repeating value you generate for each invoice. For detail, refer to the guide The UUID in e-invoicing.invoice: the full invoice document in UBL 2.1, Base64-encoded. For detail, refer to the guide Base64 encoding in e-invoicing.
The order in which you build these fields matters. You build the invoice document in UBL 2.1 first, including the previous invoice hash, the invoice counter, the digital signature, and the QR code. Then you compute the document hash with SHA-256. Then you Base64-encode the full document and put it in the field invoice. Then you put the hash in invoiceHash and the unique identifier in uuid.
Sign the invoice locally and generate the QR
SHA-256 hash and Base64 encoding
POST /invoices/reporting/single (Clearance-Status 0)
Read REPORTED within 24 hours
The three fields must be consistent with one another. The hash in invoiceHash must actually match the hash of the encoded document in invoice. And the identifier in uuid must match the identifier embedded inside the document itself. Any mismatch between the envelope and the document is bounced by validation.
Why is the invoice sent in Base64?
Because the invoice document is in XML compliant with UBL 2.1, and raw XML is not safely carried inside JSON. Base64 encoding turns the document into safe text that travels inside a JSON field without its characters breaking the envelope structure. This separates the transport layer (JSON) from the official tax document (XML) that the authority validates.
The expected response
The reporting response is always JSON. From it you first read the transport-level status code (HTTP Status), then dive into the body to read the actual result. Relying on the status code alone without reading the body is a common cause of silent integration errors.
The most important field in the reporting response is reportingStatus. On the successful path its value is REPORTED, which means the authority received the invoice and recorded it. Note the fundamental difference: in reporting you are not returned a cleared copy as in clearance; instead you are returned an acknowledgment of receipt of the invoice you originally issued and handed to the buyer.
Inside the response there is a field validationResults that carries the validation detail. It contains three lists: infoMessages for informational messages, andwarningMessages for warnings, anderrorMessages for errors. The field status inside it summarizes the overall result with the value PASS on success, or WARNING or ERROR.
The difference between success with a warning and rejection
The platform may return the invoice with the state REPORTED along with warnings in warningMessages. This means the invoice was accepted and recorded, but it has notes that are best addressed in upcoming invoices. Do not treat the warning as a rejection, but do not ignore it either, as it may turn into an error in a later version of the interface.
But when status has the value ERROR and the errorMessageslist is filled, the invoice is rejected and was not recorded. Here you need a decision: you correct the document and re-report with a new unique identifier, or you issue a credit note that cancels the effect of the defective invoice. Read each error message with its fields code andcategory andmessage to know the exact cause.
| Criterion | REPORTED | ERROR |
|---|---|---|
| Result | Reported successfully | Reporting failed |
| Warning | may be accompanied by a warning | Full rejection |
| Action | No action / correct later | Correct and resend with a new identifier |
Reading the validation messages in detail
Each message inside the validationResults lists carries a fixed structure that helps you diagnose the state precisely. The field type classifies the message among info, warning, and error. The field code is a stable code you can build programmatic logic on. The field category specifies the validation category, such as schema validation or business-rules validation. The field message is human-readable text explaining the note.
Rely on the field code not on the text of message when you build automated logic. The text is subject to change between versions and languages, whereas the code is stable. Build a map between the codes that matter to you and the appropriate action for each one, so your system handles every state without human intervention in the expected cases.
Distinguish between the two validation categories. Schema validation (XSD) confirms that the document matches the formal UBL 2.1 structure. Business-rules validation (Business Rules) confirms that the tax values are consistent and logical, such as the tax total matching the taxable base multiplied by the rate. A schema error means a problem in generating the XML, and a business-rules error means a problem in the invoice data itself.
Handling the response in your system
Reading the response is not the end, but the beginning of the handling logic. Design your system to act clearly on every state, and to keep a log of every reporting request and its result. This log is essential during audits and when tracing any gap in the invoice chain.
On success with the state REPORTED and a PASSvalidation, update the invoice’s state in your database to “reported” and store the unique identifier and the document hash. This hash becomes the input for the previous invoice hash in the next invoice, preserving the chain. Refer to the guide The Previous Invoice Hash (PIH) to understand how this chain is formed.
On rejection, do not ignore the invoice. Log the error, alert the relevant team, and take the appropriate correction path. Remember that the simplified invoice was already handed to the buyer, so the correction does not undo what happened at the point of sale; rather it corrects the record with the authority through a credit note or re-reporting.
Retrying and handling network outages
Reporting runs in the background after the sale, so it does not block the cashier experience. This means a network outage at the moment of reporting does not stop the sale, but it defers the reporting. Design a reporting queue that holds the un-reported invoices and retries them until they succeed, while respecting the twenty-four-hour window.
When retrying, do not generate a new unique identifier for the same invoice. Resend the request with the same identifier, hash, and document. Generating a new identifier for an invoice already issued creates a second invoice in the record, and breaks the counter sequence. Reporting is idempotent in logic as long as you keep the identifier stable.
Also watch for temporary transport errors versus permanent validation errors. A network error or a status code in the five-hundred range calls for a retry with an increasing backoff interval. But a validation error in validationResults is not helped by retrying with the same document; it requires correcting the document first.
A complete example of a simplified invoice journey
Imagine a restaurant cashier issuing a simplified invoice for a customer who paid cash. The invoice goes through this journey from the moment of the order to its recording with the authority, and every step has a clear owner in your system.
The point-of-sale system generates the invoice with its values, line items, and tax. The invoicing component builds the UBL 2.1 document including the unique identifier, the invoice counter, and the previous invoice hash. The component signs the document locally with the Cryptographic Stamp Identifier, generates the QR code, and prints the receipt for the customer immediately. Up to here the authority has not intervened yet.
The invoice then enters the reporting queue. The background component picks it up, computes the document hash, Base64-encodes it, assembles the JSON body, and calls POST /invoices/reporting/single with the four headers. When reportingStatus: REPORTED is returned, the component updates the invoice’s state to “reported” and stores its hash to be the input for the next invoice.
If the network drops between printing and reporting, the receipt remains valid in the customer’s hand, and the invoice stays in the queue until the retry succeeds. This separation between the moment of sale and the moment of reporting is the essence of sound B2C path design. Never tie printing the receipt to a network response.
The difference between reporting and clearance in the call
Reporting and clearance are two different endpoints despite the similarity of the request body. The difference is not in the three fields, but in the path, in one header, and in the nature of the response and its timing. Understanding this difference determines when you print the receipt and when you wait for the authority’s response.
In the path, reporting calls /invoices/reporting/single while clearance calls /invoices/clearance/single. In the header, reporting sends Clearance-Status: 0 while clearance sends Clearance-Status: 1. In timing, reporting is after delivery within a twenty-four-hour window, while clearance is before delivery and instantaneous.
As for the response, that is the deepest difference. Clearance returns you the field clearedInvoice, which is the copy of the invoice after the authority stamped it, and it is the copy you hand to the buyer. Reporting does not return a new copy; it returns only reportingStatus has the value REPORTED , because you already handed your invoice before reporting.
This difference stems from the nature of each path. B2B invoices between businesses go through clearance because the buyer is a business that needs an invoice stamped by the authority to deduct input tax. And B2C invoices for individuals go through reporting because the final consumer does not deduct tax, so it is enough for the authority to be notified later. Refer to the guide B2C invoices for individuals, technically for more detail on this path, and the guide An overview of the e-invoicing API for the broader picture.
| Criterion | Reporting (B2C) | Clearance (B2B) |
|---|---|---|
| Endpoint | /invoices/reporting/single | /invoices/clearance/single |
| Clearance-Status | 0 | 1 |
| Timing | Within 24 hours after delivery | Before delivery |
How Qoyod handles the reporting path on your behalf
Everything above describes the manual work for anyone building the integration from scratch. An accounting system compliant with Phase Two, such as Qoyod, manages this entire path on your behalf, so you do not need to write a single line of code to report your invoices.
- Managing the Cryptographic Stamp Identifier: Qoyod manages the Production CSID and its password and places them in the authentication header automatically, so you do not deal with Base64 encoding for authentication manually.
- Generating the document and signing: Qoyod builds the UBL 2.1 document, computes its SHA-256 hash, signs it locally, and generates the QR code, so you hand the buyer a compliant receipt immediately.
- Routing each invoice to its path: Qoyod distinguishes the simplified invoice (B2C) and routes it to the reporting path with the header
Clearance-Status: 0, and the tax invoice (B2B) to the clearance path, without your intervention. - Managing the window and retrying: Qoyod reports to the authority within the twenty-four-hour window, and retries automatically on network outages while keeping the unique identifier stable.
- Reading the response and preserving the chain: Qoyod reads the
REPORTEDstate and the validation results, and stores the hash of each invoice to be the input for the next, so your invoice chain stays coherent.
An important note: Qoyod transmits what you issue to the authority, and the authority is the party that verifies the invoice and records it. Qoyod does not file the tax return on your behalf, nor does it pay the tax for you. Its job is to make the invoice compliant, to report it on time, and to preserve its record.
Report B2C invoices without writing a single line of code
Qoyod manages the Cryptographic Stamp Identifier, XML generation, and the reporting path within the twenty-four-hour window on your behalf. Try Phase-Two-compliant invoicing straight from your account.
Frequently asked questions about the single reporting endpoint
What is the correct endpoint for single reporting?
The relative path is POST /invoices/reporting/single, appended after the base URL of the Sandbox or production environment. The path itself does not change between the two environments; only the base URL and the identifier change.
Which value do I put in the header Clearance-Status?
The value 0 in the reporting path. The value 1 belongs to the clearance path. This header, along with the difference in path, is what distinguishes a reporting request from a clearance request.
What does the header carry? Authorization?
authentication Basic its value is the Production CSID and its password, combined and Base64-encoded. And not a human username and password.
What are the three fields in the request body?
invoiceHash which is the SHA-256 hash of the document Base64-encoded, anduuid which is the unique identifier of the invoice, andinvoice which is the full UBL 2.1 document Base64-encoded.
How long is the window available for reporting?
Twenty-four hours from the moment the simplified invoice is issued. Missing it is a compliance violation even if the invoice is valid, so report early and do not wait for the edge of the deadline.
What should I expect in the successful response?
the field reportingStatus has the value REPORTED, with validationResults in the state PASS. You are not returned a stamped copy as in clearance; instead an acknowledgment of receipt of the invoice you originally delivered.