Qoyod
Pricing

Knowledge Base

API Integration with the Fatoora Platform

This guide is aimed at developers and technical integration teams connecting their systems to the Fatoora platform for e-invoicing in Saudi Arabia. It does not cover the conceptual story of linking an establishment to ZATCA; instead it details the APIs themselves: the endpoints, the authentication headers (Auth Headers), the request/response shapes, the status codes, and the path from Compliance to Production at the API level.

If you are looking for the broader regulatory picture of how to connect your establishment to ZATCA as a business step, refer to the guide on integrating with the Zakat, Tax and Customs Authority. This guide assumes you have read that one, and picks up where it ends: at the technical layer.

The Fatoora platform exposes four core APIs that cover the entire integration lifecycle: the Compliance API, the Onboarding / Production CSID API, the Clearance API, and the Reporting API. We walk through each one with its endpoint, its headers, its payload, and its status codes.

This guide does not repeat what integration is and why ZATCA mandated it; that is a conceptual story with its own standalone guide. Here we assume you are a developer building a real integration and want to know exactly which endpoint to call, which header to send, and which JSON shape to expect in return. Every section below carries realistically shaped request and response examples you can model against.

An overview of Fatoora’s four APIs

Before any HTTP request, it helps to understand that the four APIs split functionally into two groups. A group for setting up the device’s digital identity (Compliance and Onboarding), and a group for running invoices day to day (Clearance and Reporting). The first runs once when each device is linked; the second runs with every invoice.

This split is not merely organizational; it directly shapes your system’s design. The setup APIs are relatively slow and are called when a device is provisioned, so they can run inside a one-time setup flow while the user waits. The two operational APIs, on the other hand, are called with every sale and must be fast and non-blocking to the user experience, especially at high-frequency points of sale.

All the APIs follow the REST pattern over HTTPS and exchange data as JSON, while the invoice itself is carried as UBL 2.1-compliant XML inside a Base64-encoded field. Any request that violates this pattern is rejected at the gateway before it ever reaches business logic.

The reason for wrapping XML inside JSON is that the platform separates the transport layer from the document layer. JSON is the transport envelope that carries metadata such as the hash and the unique identifier, while XML is the official tax document that ZATCA inspects and signs. This separation means you deal with two formats in every request: you build the invoice XML per UBL, then Base64-encode it and place it in a single JSON field.

The gateway address differs between the simulation environment and the production environment. Every endpoint we show here is relative, appended to the base path of the environment you are working in. Never mix the two addresses, because sending a production invoice to the simulation environment means it was never actually cleared, and you may think everything works while there is no tax record of what you sent.

Fatoora’s four APIs
The APIs that invoicing systems interact with.
APIs

Compliance: testing the system

Onboarding: issuing the production certificate

Clearance: clearing B2B invoices

Reporting: B2C invoices within 24 hours

The first two are for one-time setup; the other two are for daily operation.

The following table summarizes the four before we dive into each one:

API Function Frequency
Compliance Issue a temporary compliance certificate and test invoices in the simulation environment Once per device at setup
Production CSID Swap the compliance certificate for a permanent production certificate that signs live invoices Once per device after passing Compliance
Clearance Clear the B2B tax invoice before it is delivered to the buyer With every B2B invoice
Reporting Report the B2C consumer invoice to ZATCA within 24 hours With every B2C invoice

Note that the difference between Clearance and Reporting is not in the shape of the API but in its timing and effect: Clearance precedes delivery and returns a cleared invoice, while Reporting follows delivery and merely confirms receipt. We detail each in its own guide: Clearance andReporting.

Authenticating requests: the Auth headers and certificates

Authenticating requests on the Fatoora platform does not rely on an OAuth token alone, but on the pairing of a digital certificate and its secret key. Every request to the invoicing APIs carries in its headers a Base64-encoded CSID certificate in the username field, and its accompanying password in the password field, following the standard Basic Authentication pattern.

The certificate here is the axis of the device’s digital identity. It starts as a temporary Compliance CSID for testing, then is replaced by a Production CSID for live operation. The details of this certificate and how to generate it are explained in the CSID certificate.

guide. It is important to distinguish between two roles the certificate plays. The first role is authenticating the request itself at the gateway, and here the certificate is sent in the Authorization header to prove that whoever is knocking is a known, registered device. The second role is signing the invoice, and here the private key associated with the certificate is used to create the cryptographic stamp inside the XML. The two roles are separate: the first at the transport level, the second at the document level.

The cryptographic stamp is generated with the ECDSA algorithm on the secp256k1 curve. Never send the private key in any request; instead use it locally to sign the invoice hash, then send the signature within the signed XML. This means the security of your private key is your own responsibility, and any leak of it means another party can issue invoices in your establishment’s name.

This is an example of standard authentication request headers. Note that the value of Authorization is the result of Base64-encoding “identifier:password”, and that OTP is sent only in the first Compliance and Production requests, not with every invoice:

POST /invoices/clearance/single HTTP/1.1
Host: gw-fatoora.zatca.gov.sa
Content-Type: application/json
Accept: application/json
Accept-Language: ar
Accept-Version: V2
Authorization: Basic VFVsSlEyd...<Base64(CSID:secret)>
Clearance-Status: 1

The header Accept-Version: V2 is mandatory and specifies the API version. And the header Clearance-Status tells the platform you are requesting an actual clearance, not just a check. Omitting either one returns a rejection at the gateway level before the invoice is processed.

API request layers
What every request goes through before it reaches the Fatoora platform.
1

Secure HTTPS connection

2

Accept-Version: V2 header

3

Authorization: Basic (CSID) authentication

4

Business logic and ECDSA signing

Any error in the layers returns a status code (401/422) with details.

The Compliance API

The Compliance API is the first API a device touches. Its purpose is to issue a temporary compliance certificate (Compliance CSID) and then use it to test sample invoices in the simulation environment before production is allowed. This certificate does not sign real invoices; it proves that your system produces invoices conforming to the specification.

The path begins by generating a Certificate Signing Request (CSR) locally, then sending it to the following endpoint along with the one-time password (OTP) you obtain from the Fatoora portal:

POST /compliance HTTP/1.1
Host: gw-fatoora.zatca.gov.sa
Content-Type: application/json
Accept-Version: V2
OTP: 123456

{
  "csr": "<Base64-encoded-CSR>"
}

On success, the platform returns the temporary compliance certificate, the request ID, and the accompanying password. These three values are what you use in the subsequent invoice-testing requests:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "requestID": 1234567890123,
  "dispositionMessage": "ISSUED",
  "binarySecurityToken": "<Base64-Compliance-CSID>",
  "secret": "<Base64-secret>"
}

After that, you send sample invoices to the compliance-check endpoint. Your system must pass a set of mandatory cases: a standard tax invoice, a simplified invoice, and their credit and debit notes. A device is not granted a production certificate before passing all the required cases:

POST /compliance/invoices HTTP/1.1
Host: gw-fatoora.zatca.gov.sa
Content-Type: application/json
Accept-Version: V2
Authorization: Basic <Base64(Compliance-CSID:secret)>

{
  "invoiceHash": "<SHA-256-Base64>",
  "uuid": "3cf5ee18-ee25-44ea-a444-2c37ba7f28be",
  "invoice": "<Base64-encoded-UBL-XML>"
}

The response tells you the check result for each rule separately, so you know precisely where a sample failed if it did. We show the shape of this response in the status-codes section later.

The value of invoiceHash in the request is neither optional nor cosmetic. It is the SHA-256 hash of the canonicalized XML before encoding, computed per specific canonicalization rules defined in ZATCA’s specification. Any difference between the hash you send and the hash the platform computes from the attached XML means an immediate rejection, because the platform treats that as evidence of possible tampering with the content.

The unique identifier uuid must be unique for every invoice across the entire establishment, not only at the device level. Build its generation logic so it is impossible to repeat even with multiple points of sale, because a repeated identifier disrupts the hash chain and breaks the linking of an invoice to its predecessor.

Remember that the compliance certificate is limited in validity and purpose. Do not use it to issue live invoices under any circumstance; it is for testing in the simulation environment only. As soon as your device passes the check, move immediately to extracting the production certificate via the next API.

The Production CSID API

After fully passing Compliance, the device requests an upgrade of its temporary certificate to a permanent production certificate. This certificate (Production CSID) is the one that signs every live invoice, and is what distinguishes a cleared invoice from a rejected one. Every device, branch, or point of sale needs its own production certificate.

The production request is simple in shape: it carries the ID of the compliance request you succeeded with, and is authenticated with the compliance certificate itself:

POST /production/csids HTTP/1.1
Host: gw-fatoora.zatca.gov.sa
Content-Type: application/json
Accept-Version: V2
Authorization: Basic <Base64(Compliance-CSID:secret)>

{
  "compliance_request_id": "1234567890123"
}

The response returns the production certificate and its password. From this moment you stop using the compliance certificate in production entirely, and replace it with the production certificate in all Clearance and Reporting requests:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "requestID": 9876543210987,
  "dispositionMessage": "ISSUED",
  "binarySecurityToken": "<Base64-Production-CSID>",
  "secret": "<Base64-production-secret>"
}

Renewing the production certificate as it nears expiry is done via the same endpoint with a PATCH request instead of POST and a new OTP header. Build logic that tracks each device’s certificate expiry date and renews it early, because expiry stops invoice signing immediately.

Multiple devices is the most overlooked aspect at this stage. If your establishment has five branches and each branch has two points of sale, you are facing ten independent digital identities, each of which goes through the Compliance then Production journey separately. Design your certificate storage so it links each certificate to its device, its branch, and its expiry date, and avoid any mix-up between them, because signing one branch’s invoice with another branch’s certificate breaks source traceability.

Protect the password accompanying the production certificate as you would any sensitive production secret. Store it in an encrypted secrets store, not in the database in plain text nor in configuration files. Access to it equals the ability to issue cleared invoices in the establishment’s name, so treating it with anything less is both an operational and a security risk.

Start today

A ready-made integration with the Fatoora platform without writing a single line

It manages compliance and production certificate (CSID) constraints and both the Clearance and Reporting paths automatically, so your invoices are issued Phase Two-compliant without building an API integration from scratch.

Start your free trial and connect your establishment to the Fatoora platform

The Clearance API

Clearance is the mandatory path for the business-to-business (B2B) tax invoice. You send the invoice to the platform, ZATCA inspects it, signs it, and returns it cleared, and only then are you entitled to deliver it to the buyer. Any B2B invoice that has not passed through this API is not a compliant invoice.

The endpoint carries the header Clearance-Status: 1 to signal an actual clearance request. The request payload consists of the hash value (Hash), the unique identifier (UUID), and the invoice as Base64-encoded XML:

POST /invoices/clearance/single HTTP/1.1
Host: gw-fatoora.zatca.gov.sa
Content-Type: application/json
Accept-Version: V2
Clearance-Status: 1
Authorization: Basic <Base64(Production-CSID:secret)>

{
  "invoiceHash": "9f8c7d6e5b4a...",
  "uuid": "0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
  "invoice": "<Base64-encoded-UBL-XML>"
}

On success, the platform returns the cleared invoice signed with ZATCA’s stamp, along with the clearance status. The field clearedInvoice is the signed XML version that you must deliver to the buyer instead of the original version:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "validationResults": {
    "status": "PASS",
    "warningMessages": [],
    "errorMessages": []
  },
  "clearanceStatus": "CLEARED",
  "clearedInvoice": "<Base64-signed-UBL-XML>"
}

The response may come back with the status CLEARED_WITH_WARNINGS. This means the invoice was cleared but carries notes that must be addressed before upcoming invoices, such as an imprecise tax identifier format. Address the warnings and do not ignore them, as they may turn into blocking errors in later versions of the specification. Full details in the guide Clearance.

A critical design point in the clearance path is that the invoice you deliver to the buyer is not the version you issued yourself, but the version returned in the field clearedInvoice. This version carries ZATCA’s stamp over your stamp, and it alone is the compliant document. Delivering your own uncleared original is a common mistake that leaves the buyer holding an invoice with no legal value.

And since clearance is synchronous and blocking by nature, account for the response time in the user experience. Do not freeze the point-of-sale interface waiting for the platform’s reply with no indicator; instead show a clear waiting state and set a reasonable maximum timeout. If the timeout is exceeded, treat the case as a temporary, retryable failure, not a silent success.

Do not confuse invoice validation (Clearance-Status: 0) with the actual clearance request (Clearance-Status: 1). The first returns a validation result without clearance, and is useful in testing. The second is the request that actually clears the invoice in production. Sending a validation request in production thinking it is clearance leaves your invoice uncleared.

The Reporting API

Reporting is the path for the simplified tax invoice aimed at the consumer (B2C). Here the invoice is delivered to the buyer immediately at the point of sale, then ZATCA is reported to about it within 24 hours at most. You do not wait for prior clearance, because the transaction has already been completed.

The request shape is very close to Clearance, but the endpoint is different and does not carry the header Clearance-Status, because the goal is to prove reporting, not request clearance:

POST /invoices/reporting/single HTTP/1.1
Host: gw-fatoora.zatca.gov.sa
Content-Type: application/json
Accept-Version: V2
Authorization: Basic <Base64(Production-CSID:secret)>

{
  "invoiceHash": "1a2b3c4d5e6f...",
  "uuid": "9z8y7x6w-5v4u-3t2s-1r0q-9p8o7n6m5l4k",
  "invoice": "<Base64-encoded-UBL-XML>"
}

The response merely confirms receipt of the report and the validation status. It does not return an invoice signed by ZATCA, because the version the customer holds is valid from the moment it is issued:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "validationResults": {
    "status": "PASS",
    "warningMessages": [],
    "errorMessages": []
  },
  "reportingStatus": "REPORTED"
}

The status REPORTED_WITH_WARNINGS may come back with the same logic. The essential difference from Clearance is that a reporting failure does not block the transaction because it already happened, but it exposes you to a violation if you do not address it within the deadline. An expanded explanation in the guide Reporting.

The asynchronous nature of reporting lets you design it entirely differently from clearance. There is no need to halt the sale waiting for the platform’s reply, since the customer took their invoice. Instead, issue the invoice and deliver it, then place the reporting request in a background queue that handles sending and retries on temporary failure. This preserves point-of-sale speed and respects the 24-hour deadline at once.

But do not make the background queue an excuse for negligence. Any B2C invoice not reported within the deadline turns into a potential violation. Monitor the queue length and the age of its oldest item, and fire an alert if any item approaches the 24-hour limit. Silently delayed reporting is more dangerous than a visible failure, because it accumulates without anyone noticing.

The Quick Response (QR) code on a B2C invoice must be complete before delivery, not after reporting. The customer may scan the code to verify the invoice, so it must not wait for the platform’s reply. Your system generates the code locally from the invoice data and your cryptographic stamp in Base64-encoded TLV format, without any dependence on ZATCA’s reply.

Status codes and error handling

Every Fatoora API returns a standard HTTP status code, accompanied in rejection cases by a response body that details the reason for rejection for each validation rule. Understanding these codes determines the retry logic in your system: when to resend, and when to stop and alert the user.

The platform does not stop at a numeric code. It attaches a detailed list of messages split into warnings and errors, and each message carries a classification code that usually begins with BR-KSA for the Saudi rules, or BR for the general UBL rules. This split lets your system read the reason programmatically and present it to the user in understandable language instead of a raw code.

The difference between a warning and an error is fundamental to your system’s design. An error blocks the invoice so it is neither cleared nor reported, so you must halt the transaction and correct it. A warning does not block the invoice but alerts you to a violation that may tighten later. The correct handling is to log warnings in a reviewable record and address them periodically, not to ignore them because they did not stop issuance.

This is an example of a rejection response body from the Clearance API, showing how errors are detailed for each rule:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "validationResults": {
    "status": "ERROR",
    "warningMessages": [],
    "errorMessages": [
      {
        "type": "ERROR",
        "code": "BR-KSA-44",
        "category": "KSA",
        "message": "Invalid VAT registration number format."
      }
    ]
  },
  "clearanceStatus": "NOT_CLEARED"
}

The following table maps the code to the recommended action in your system:

Code Meaning Recommended action
200 Cleared or reported successfully Deliver the cleared version and store it
202 Accepted with warnings Address the warning before upcoming invoices
400 Invalid request Fix the payload and do not resend before the fix
401 Authentication failure Verify the certificate and password
422 Validation rules failure Read errorMessages and correct the invoice
500 and 503 Server error or service unavailable Retry with exponential backoff

Practical rule: errors in the 4xx range originate from your own data, so there is no point resending without correction. As for 5xx, they originate from the platform, and retrying with exponential backoff is the correct behavior. Do not resend a failed B2C invoice without limit; instead stop it after a number of attempts and alert the user to avoid silently exceeding the 24-hour deadline.

Log every request and response in an auditable record that links the invoice to its unique identifier, the returned status code, and the message text. In a dispute or a review from ZATCA, this record is your evidence that you sent, at what time, and with what result. An incomplete log means you prove your compliance with words, not with a trace.

Distinguish in your logic between transient failure and permanent failure. A network outage or a 503 reply is a transient failure that deserves a retry. A 422 rejection due to a validation rule is a permanent failure that a retry does not solve; only correcting the invoice does. Mixing the two types leads either to flooding the platform with futile resends, or to abandoning an invoice that would have succeeded had you resent it.

Finally, do not show the user the raw error code. Translate the code BR-KSA into an understandable message that tells the accountant exactly what to fix: “The buyer’s tax number is in an incorrect format” is far clearer than reciting a technical code. This layer of translation is what separates a usable integration from one that confuses its users.

The transition from Compliance to Production at the API level

If we combine the four APIs into a single path, we get the device’s journey from the first request to the first live invoice. This journey is purely technical, executed once per device, then not repeated except at certificate renewal.

The Compliance-to-Production journey via API
The steps of the transition from testing to actual issuance.
1

Generate the CSR

2

POST /compliance with OTP

3

Test invoices in the simulation

4

POST /production/csids

5

Begin with Clearance or Reporting

The simulation environment is separated from production by issuing the production certificate after testing.

The steps in technical order:

  • Generate a Certificate Signing Request (CSR) inside your system for each device.
  • Call POST /compliance with an OTP to obtain the temporary compliance certificate.
  • Test sample invoices via POST /compliance/invoices until all mandatory cases pass.
  • Call POST /production/csids with the successful compliance request ID to obtain the production certificate.
  • Swap the compliance certificate for the production certificate in all live requests.
  • Run invoices: Clearance for B2B and Reporting for B2C with every transaction.

The separation between the simulation environment and the production environment is fundamental. Test everything in the simulation first; it is designed to receive your errors with no compliance impact. Do not move a device to production before it passes all compliance cases, because production errors leave a real tax trace. Review the details of the two environments in the guide CSID certificate and the guide integrating with ZATCA.

Note that this journey is repeated in full for every new device you add later. Opening a new branch or adding a point of sale means repeating the Compliance and Production steps for that device alone, without touching the operating devices. Design your setup flow so it is self-repeatable for each device, not a manual procedure executed once and its mechanism forgotten.

Also put logic in place for what comes after go-live. Renewing the certificate before it expires, reissuing a certificate for a device that was decommissioned or replaced, and handling changes to the establishment’s data at ZATCA are all events that call for returning to the setup APIs. A mature integration does not stop at the first successful invoice; it manages the certificate’s full lifecycle.

Where does Qoyod fit in this layer?

Qoyod is an e-invoicing solution compliant with Phase Two of e-invoicing. In practice, this means Qoyod performs on your behalf everything we explained in this guide without you writing a single line of API.

Qoyod generates the invoice XML in UBL 2.1 format with the Quick Response (QR) code, the cryptographic stamp, and the hash chain. It manages the compliance and production certificates (CSID) for each device automatically. It handles both the Clearance path for business-to-business invoices and the Reporting path for consumer invoices. Qoyod also added tax-identifier format validation at the moment of entry, catching one of the most common causes of ZATCA warnings before sending.

The role here is clear: Qoyod is the launchpad to the Fatoora platform, not a replacement for it. The Fatoora platform is mandatory, and Qoyod takes on the entire technical layer on your behalf, so you focus on your business instead of building and maintaining an integration.

Guides

Continue your learning journey

Explore the rest of Qoyod’s guides, or start applying what you’ve learned.

Live webinars hosted by the Qoyod team to help you use the software easily and answer your questions.

Discover Qoyod’s latest updates, ongoing improvements, and new features in one place.

Our team is ready to help you and provide instant support for any issue you face, around the clock.