{"id":63,"date":"2026-06-07T20:30:00","date_gmt":"2026-06-07T17:30:00","guid":{"rendered":"https:\/\/fsaeed.blog\/?p=63"},"modified":"2026-10-31T00:18:50","modified_gmt":"2026-10-30T21:18:50","slug":"saudi-e-invoicing-zatca-for-developers","status":"publish","type":"post","link":"https:\/\/fsaeed.blog\/index.php\/2026\/06\/07\/saudi-e-invoicing-zatca-for-developers\/","title":{"rendered":"Saudi E-Invoicing (ZATCA) Explained for Developers"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Hello, tech enthusiasts! \ud83d\udc4b<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Your manager says: &#8220;Our billing system must support Saudi e-invoicing.&#8221; You search online and find words like Fatoora, clearance, CSID, UBL and hash chain. It feels like a new language!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not worry. Today we explain each word simply and write a little real code. This is a technical guide, not legal or tax advice. Rules change, so always check the official site of the Zakat, Tax and Customs Authority (ZATCA) at <a href=\"https:\/\/zatca.gov.sa\">https:\/\/zatca.gov.sa<\/a> before you build.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What you need<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Basic knowledge of invoices (seller, buyer, VAT, total)<\/li>\n\n\n<li>Python 3 for the code example<\/li>\n\n\n<li>An account on the ZATCA developer portal (linked from the official site)<\/li>\n\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Understand the two phases<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The Saudi e-invoicing program is called <strong>Fatoora<\/strong>. It came in two phases.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Phase 1, &#8220;Generation&#8221;<\/strong> (started December 2021). Businesses must create invoices with a computer system, not by hand. Simplified invoices need a QR code. The invoice does not go to ZATCA yet.<\/li>\n\n\n<li><strong>Phase 2, &#8220;Integration&#8221;<\/strong> (started January 2023). Your system connects to ZATCA&#8217;s platform and sends invoices to it. Businesses join in waves, and ZATCA tells each group its date.<\/li>\n\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Know the two invoice types<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Standard tax invoice<\/strong>: for business to business (B2B). The buyer is a registered company.<\/li>\n\n\n<li><strong>Simplified tax invoice<\/strong>: for business to customer (B2C), like a shop receipt.<\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">In Phase 2 they travel in different ways:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Clearance<\/strong> (standard invoices): you send the invoice to ZATCA <strong>first<\/strong>. ZATCA checks it and adds its stamp. Only then do you give it to the buyer. Like passport control: no stamp, no entry.<\/li>\n\n\n<li><strong>Reporting<\/strong> (simplified invoices): you give the invoice to the customer right away, and then report it to ZATCA within 24 hours.<\/li>\n\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Learn the invoice format<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In Phase 2, the official invoice is an XML file in the <strong>UBL 2.1<\/strong> format. XML is text with labels around each value, like <code>&lt;Amount&gt;100.00&lt;\/Amount&gt;<\/code>. UBL is an international standard for those labels, and ZATCA adds its own rules. You can give the customer a PDF, but for Phase 2 it should be a PDF\/A-3 file with the XML inside it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Each invoice also has a <strong>UUID<\/strong> (a unique ID), an <strong>invoice counter<\/strong> that goes up by one each time, and a <strong>QR code<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 4: Build a Phase 1 QR code<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The QR code content uses a format called <strong>TLV<\/strong>: Tag, Length, Value. Think of boxes in a row: each has a number (tag), a size (length) and content (value). The five Phase 1 tags are: seller name, VAT number, time stamp, total with VAT, and VAT amount. The result is encoded as Base64 text.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import base64\n\ndef tlv(tag, value):\n    data = value.encode(\"utf-8\")      # length is in bytes, not letters\n    return bytes([tag, len(data)]) + data\n\ndef phase1_qr(seller, vat_number, timestamp, total, vat):\n    payload = (\n        tlv(1, seller)\n        + tlv(2, vat_number)\n        + tlv(3, timestamp)\n        + tlv(4, total)\n        + tlv(5, vat)\n    )\n    return base64.b64encode(payload).decode(\"ascii\")\n\n# Example values only\nprint(phase1_qr(\"Example Trading Co\", \"300000000000003\",\n                \"2026-06-07T12:30:00Z\", \"115.00\", \"15.00\"))<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Put the printed text into any QR code library. Arabic names also work, because length is counted in UTF-8 bytes. In Phase 2, the QR code gets more tags, such as the invoice hash and the digital signature.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 5: Understand the stamp and the hash chain<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Phase 2 adds two security ideas.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Cryptographic stamp<\/strong>: your system signs each invoice with a private key. Like a wax seal, it proves the invoice is yours and unchanged.<\/li>\n\n\n<li><strong>Hash chain<\/strong>: a hash is a short &#8220;fingerprint&#8221; of a file (ZATCA uses SHA-256). Each new invoice includes the hash of the previous invoice. If someone deletes or changes an old invoice, the chain breaks and it shows.<\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For the exact hashing and signing steps, follow ZATCA&#8217;s technical documents and sample files.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 6: Onboarding with a CSID<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Your system must be registered first. The certificate ZATCA gives you is called a <strong>CSID<\/strong> (Cryptographic Stamp Identifier). The flow is:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create a private key and a certificate request (CSR) with your company details.<\/li>\n\n\n<li>Get a one-time password (OTP) from the Fatoora portal.<\/li>\n\n\n<li>Ask for a <strong>compliance CSID<\/strong> and send test invoices. ZATCA checks them.<\/li>\n\n\n<li>When the tests pass, ask for a <strong>production CSID<\/strong> and start live work.<\/li>\n\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Practice first in the <strong>sandbox<\/strong> on the developer portal. Nothing you send there counts as a real invoice.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Common problems<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>The QR code does not scan in the official app.<\/strong> Check that each length is in bytes, and that the amounts have two decimals.<\/li>\n\n\n<li><strong>ZATCA rejects the invoice hash.<\/strong> The XML was changed after hashing, even by one space. Hash the final file.<\/li>\n\n\n<li><strong>The counter or chain breaks after a crash.<\/strong> Save the counter and last hash in your database, in the same step as the invoice.<\/li>\n\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Wrap-up<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Now the strange words have meaning: two phases, two invoice types, UBL XML, a QR code, a stamp, a chain, and a CSID. If the official site says something different from this post, trust the official site.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next idea: download the official sample invoices and SDK from the developer portal, and test your XML against them.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A simple map of Saudi e-invoicing rules for developers who build billing systems.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[61,12,64,63,62],"class_list":["post-63","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-e-invoicing","tag-python","tag-saudi","tag-xml","tag-zatca"],"_links":{"self":[{"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/posts\/63","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/comments?post=63"}],"version-history":[{"count":1,"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/posts\/63\/revisions"}],"predecessor-version":[{"id":70,"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/posts\/63\/revisions\/70"}],"wp:attachment":[{"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/media?parent=63"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/categories?post=63"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/tags?post=63"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}