ZATCA-Compliant Invoicing for an Intermediary Services Platform¶
Six invoicing approaches and their implementation on Invora.
- Audience
- Platform engineering and integration teams.
- Scope
- How to invoice the customer-facing service supply and the platform's inter-party charge, in a ZATCA-compliant way, across six candidate cases, and how each is built on Invora using REST (gRPC-JSON transcoding).
- Basis
- ZATCA E-Invoicing Phase 2 (Integration); VAT Implementing Regulations (VAT IR) as amended April 2025.
- Sources
- Every regulatory statement maps to a primary-source file and an article/section locator in
regulations/SOURCES.md.
Not legal or tax advice
Technical and regulatory reference only. Confirm the classification questions in §11 with a qualified KSA VAT adviser before implementation.
Contents¶
- Parties and the two invoices
- Summary of constraints
- Assumptions and VAT permutations
- Regulatory rules that shape the design
- The Invora platform model
- The cases
- Permutation matrix
- Decision framework
- Implementation notes
- Tap payment collection
- Open questions for the legal and tax team
- References
1. Parties and the two invoices¶
| Symbol | Party | Role |
|---|---|---|
| SP | Service Provider | Performs the service. |
| P | Platform (the startup) | Intermediary that organises the transaction and charges a per-transaction fee. |
| C | Customer | Recipient of the service. A consumer in most cases; sometimes a business. |
Each transaction produces up to two e-invoices:
- Invoice 1 is the customer-facing supply: the seller of record bills C.
- Invoice 2 is the inter-party invoice between P and SP.
The seller, buyer and issuer of each invoice, and what each invoice represents, change with the case. Invoice 2 is the platform fee in some cases and SP's service cost to P in others. Its issuer and aggregation vary too. The first three cases set the two legal positions (who supplies C). Cases 4 to 6 are issuance and aggregation variants of them.
In the table below, "issuer" is the tenant that signs and submits the e-invoice (defined fully after the table). On the inter-party invoice, when the issuer is the buyer of that invoice, the invoice is self-billed (Art. 53(2)).
| Case | Invoice 1: seller, buyer (issuer) | Invoice 1 represents | Invoice 2: seller, buyer (issuer) | Invoice 2 represents |
|---|---|---|---|---|
| Case 1 | SP to C (issuer SP) | SP's service to C | P to SP (issuer P) | Platform fee (P's commission) |
| Case 2 | P to C (issuer P) | P's resold service to C | SP to P (issuer P, self-billed) | SP's service cost to P; fee is P's margin |
| Case 3 | SP to C (issuer P, third-party) | SP's service to C | P to SP (issuer P) | Platform fee (P's commission) |
| Case 4 | P to C (issuer P) | P's resold service to C | SP to P (issuer SP) | SP's service cost to P; fee is P's margin |
| Case 5 | SP to C (issuer SP, or P as third-party; see Case 3) | SP's service to C | P to SP (issuer SP, self-billed) | Platform commission |
| Case 6 | inherits Invoice 1 from Case 1/3/4 | service to C | P to SP (issuer P, monthly summary) | Aggregated platform fees |
flowchart LR
SP["Service Provider (SP)"] -- "performs the service" --> C["Customer (C)"]
P["Platform (P)"] -- "organises / facilitates" --> SP
P -- "organises / facilitates" --> C
The issuer (the issuing tenant) is the tenant whose credentials make the API call: it generates, signs and submits the e-invoice to ZATCA. Throughout this guide "issuer" and "issuing tenant" mean the same party. The issuer is distinct from the seller, whom ZATCA treats as the legally responsible supplier regardless of who issues the document.
Invora identifies the issuing tenant by its self-party (the tenant's own legal identity as a seller: its registered name, VAT number and address) and compares it to the seller and buyer you send; that comparison selects self-billed vs third-party. Cases 2 and 3 rely on that separation.
2. Summary of constraints¶
Which case is lawful for a given transaction turns on two questions: who is the real supplier to C, and whether the marketplace deemed-supplier rule applies. All six cases are expressible on Invora and each has a ZATCA/VAT basis, but they are not interchangeable.
Deemed-supplier rule (VAT IR Art. 47(3), effective 1 January 2026)
A marketplace that facilitates a supply is treated as the supplier to C (and must report and remit the VAT) when either:
- (A) it facilitates the electronic supply of services to KSA customers by a non-resident supplier, or
- (B) it facilitates the supply of goods or services by a non-registered resident supplier.
Scenario A is limited to electronic (digital) services. A non-resident supplier of a non-electronic, physically-performed service (e.g. on-site work) does not, by itself, trigger the rule.
Qualifying for the exception requires avoiding all five control factors:
- set the terms
- determine the consideration
- collect payment
- handle complaints
- offer promotions or compensation
The exception also differs by scenario (A: the agreement names the supplier as principal; B: disclosure plus a direct supplier-to-customer contract); see §4.6. A platform that collects payment and sets terms fails the control factors, so it does not meet the exception.
Consequence: for non-resident or non-registered SPs, the law forces the Case 2 shape regardless of commercial preference.
When the rule does not apply (every SP is a VAT-registered resident), the choice becomes a VAT-agency and operational decision:
| Case | Who supplies C | Inter-party invoice | Mechanism | Trade-off |
|---|---|---|---|---|
| 1 | SP (disclosed agent) | P invoices SP a fee | Ordinary invoicing | Lowest classification risk; issuance spread across SPs |
| 3 | SP (disclosed agent) | P invoices SP a fee | Third-party issuance (Art. 53(3)) | Same VAT position as Case 1, but P operates issuance: best ergonomics |
| 2 | P (undisclosed / deemed) | P self-bills SP for the service | Self-billing (Art. 53(2)) | Strongest platform control; largest VAT footprint for P; strict conditions |
Cases 4 to 6 keep these VAT positions and vary only issuance and aggregation:
- Case 4 is the principal model (SP invoices P normally).
- Case 5 self-bills the commission.
- Case 6 issues one monthly summary fee invoice per SP.
Invora surfaces
Three things wire the platform together:
- Onboarding: SPs join as Connected Businesses (
POST /api/v2/identity/connected-businesses). - Write surface: the Simple API (
/api/v1/simple/{invoices, credit-notes, debit-notes}) issues an invoice where the seller is the issuing tenant by default; an optionalsellerparty (the supplier of record) lets the same call express self-billed and third-party issuance, andisSummary: trueproduces a native summary invoice. - Derivation: the backend derives the ZATCA subtype and transaction flags from your inputs; you never send a type code.
Detail in §5.
3. Assumptions and VAT permutations¶
The analysis covers the full permutation space:
| Dimension | Permutations |
|---|---|
| Customer (C) | Non-registered consumer → simplified invoice, reporting model. VAT-registered business → standard tax invoice, clearance model. |
| Service Provider (SP) | VAT-registered resident → standard invoices, clearance. Non-registered (below threshold) or non-resident → triggers the deemed-supplier rule and changes self-billing eligibility. |
| Platform (P) | VAT-registered resident taxable person. P must be registered to charge VAT on its fees and to operate as an issuer in KSA. |
Working assumptions (flag any that are wrong, see §11):
- Transactions are in SAR; supplies are standard-rated at 15% unless a specific zero-rating or exemption applies.
- P is onboarded to ZATCA Phase 2 with a production CSID and acts as the issuing solution for the flows it generates.
- The per-transaction platform fee is a taxable supply of intermediation services by P.
4. Regulatory rules that shape the design¶
Each rule below maps to a primary source. Canonical URLs and per-claim locators are in regulations/SOURCES.md.
4.1 Two invoice types, two submission models¶
ZATCA Phase 2 handles Standard Tax Invoices (B2B) and Simplified Tax Invoices (B2C) differently.
- Clearance applies to Standard Tax Invoices. ZATCA verifies the invoice and inserts the cryptographic stamp before it is valid to give to the buyer (blocking). Clearance does not apply to simplified invoices. (Detailed Guideline §2.15.)
- Reporting applies to Simplified Tax Invoices. The issuer transmits them to the FATOORA Portal within 24 hours of issuance (async, non-blocking). (Detailed Guideline §2.16.)
For the cases below: Invoice 1 to a consumer is a simplified invoice (reporting). Invoice 1 to a business, and every platform-fee invoice (B2B), is a standard invoice (clearance). This drives latency and failure handling in every flow.
4.2 Who is the supplier: disclosed vs undisclosed agent¶
Because P is an intermediary, the threshold question is whether P acts as a disclosed agent (SP is the supplier to C) or an undisclosed agent / commissionaire (P is the supplier to C). The VAT Agents Guideline defines both.
- A disclosed agent acts for a principal where the agency relationship is disclosed to the third party. The principal (SP) is the supplier; the agent (P) supplies only an agency service (its fee).
- An undisclosed agent acts in its own name and does not disclose that it acts for a principal. The guideline calls this a "commissionaire" and provides that the agent "is treated as a Principal for VAT purposes" (§6.2). P is then treated as buying the service from SP and re-supplying it to C.
Cases 1 and 3 are disclosed-agent structures. Case 2 is an undisclosed-agent structure. The classification depends on the actual contracts, branding, and who the customer understands they are buying from. It is a legal determination, not an engineering one.
4.3 Third-party invoicing (VAT IR Art. 53(3))¶
A third party may issue a tax invoice on behalf of the supplier. The supplier stays the supplier of record and keeps the liability:
"Subject to the Authority's approval, a Taxable Person may issue Tax Invoices through a third-party provided that all obligations … set forth in the Law and these Regulations are satisfied. The Supplier shall be responsible for the accuracy of the information shown on the Tax Invoice and for reporting Output Tax on the supply." (VAT Implementing Regulations, Article 53(3)).
ZATCA's e-invoicing scope confirms the third party is itself an in-scope issuer (Detailed Guideline §1.5). This is the basis for Case 3: P issues Invoice 1, SP remains the seller of record, and the invoice is classified third-party.
4.4 Self-billing (VAT IR Art. 53(2))¶
The buyer of a supply may issue the tax invoice on behalf of the supplier (self-billing), under four conditions. Art. 53(2) calls this party the Taxable Customer: the buyer of the supply who receives and pays for it, not the end customer C.
"Subject to the Authority's approval, a Taxable Customer may issue Tax Invoices on behalf of a Supply made by a Taxable Supplier provided that a prior agreement between such Supplier and Customer has been made to this effect; that the Tax Invoice state that it was issued by the Customer on behalf of the Supplier; and that the Supplier and Customer are registered with the Authority for VAT purposes. … a certification by the Supplier that they will not issue invoices in respect of the Supplies for those Tax Invoices were issued." (VAT Implementing Regulations, Article 53(2)).
Self-billing requires all four conditions:
- a prior agreement between the supplier and the buyer;
- the invoice states it was issued by the buyer on behalf of the supplier;
- both parties are VAT-registered with the Authority;
- the supplier certifies it will not also issue its own invoice for those supplies.
ZATCA's e-invoicing implementation standard adds that self-billing is B2B-only and is not permitted for exports. This is the basis for Invoice 2 of Case 2: P, as the buyer of SP's supply, issues the self-billed invoice, but only when both SP and P are VAT-registered.
4.5 ZATCA invoice subtype and transaction flags (derived)¶
This section is a regulatory reference for ZATCA's invoice subtype and transaction flags. On Invora you do not send these flags. The Simple API derives them from your inputs: the buyer's VAT status, the optional seller party and its relationship to the issuing tenant, and the isSummary boolean. The mapping from your inputs to the derived classification is in the table at the end of this section.
ZATCA classifies each invoice by a subtype plus a set of transaction flags, encoded as a seven-character code (NNPNESB) defined in rule BR-KSA-06.
| Position | Meaning | Values |
|---|---|---|
1 and 2 (NN) |
Invoice subtype | 01 standard tax invoice, 02 simplified tax invoice |
3 (P) |
Third-party transaction | 0 / 1 |
4 (N) |
Nominal transaction | 0 / 1 |
5 (E) |
Exports transaction | 0 / 1 |
6 (S) |
Summary transaction | 0 / 1 |
7 (B) |
Self-billed transaction | 0 / 1 (cannot be 1 when position 5 = 1) |
ZATCA's published sample documents (indexed in SOURCES.md) use these codes:
| Code | Meaning |
|---|---|
0100000 |
standard, no flags |
0200000 |
simplified, no flags |
0110000 |
standard + third-party (pos 3) |
0100001 |
standard + self-billed (pos 7) |
0100010 |
standard + summary (pos 6) |
In both the third-party and self-billed cases, the seller remains the actual supplier of record; the transaction flag (not a change of seller) signals third-party or self-billed issuance. A simplified third-party invoice (Case 3 to a consumer) uses 0210000 (subtype 02 plus position-3 flag), derived from BR-KSA-06; ZATCA's sample set ships only the standard third-party form 0110000.
How Invora derives the flag from your inputs. You send business facts on the request body; the backend compares them (in particular, the seller party against the issuing tenant's self-party) and derives the correct classification:
| What you set on the invoice | Resulting ZATCA classification |
|---|---|
buyer carries vatRegistrationNumber |
subtype 01 (standard, clearance): positions 1 and 2 |
buyer has no vatRegistrationNumber |
subtype 02 (simplified, reporting): positions 1 and 2 |
seller omitted |
normal invoice: issuing tenant is the supplier; no third-party / self-billed flag |
seller present and issuing tenant is the buyer |
self-billed flag set: position 7 (B) |
seller present and issuing tenant is neither buyer nor seller |
third-party flag set: position 3 (P) |
isSummary: true |
summary flag set: position 6 (S); combinable with the above |
The caller never sets a type code; the classification is computed on freeze and visible in the document's regulation metadata after submission.
4.6 Deemed-supplier rule (VAT IR Art. 47(3))¶
KSA amended the VAT IR (Official Gazette 18 April 2025). Most changes took effect on 18 April 2025; the electronic-marketplace deemed-supplier provision (Article 47, Paragraph 3) took effect on 1 January 2026.
Trigger. As deemed supplier, the marketplace is treated as having acquired and re-supplied the service on its own account, and is responsible for reporting and remitting the VAT.
This applies when it facilitates either (Scenario A) the electronic supply of services to customers in Saudi Arabia by a non-resident supplier, or (Scenario B) the supply of goods or services by a non-registered resident supplier. Scenario A is confined to electronic (digital) services; it does not extend to physically-performed or on-premises services by a non-resident.
Exception. The exception is not a single test; it differs by scenario. Both scenarios share the same five control factors the marketplace must avoid:
- set the terms
- determine the consideration
- collect payment
- handle complaints
- offer promotions or compensation
Source: VAT IR amendments, PwC summary p. 4
Scenario A exception applies only if the agreement explicitly states the non-resident is the principal supplier (supported by appropriate invoice and receipt) and the marketplace does not engage in any of the five control factors. Scenario B exception applies only if it is clearly disclosed (in the contract, invoice and receipt) that the supply is by a non-registered resident, there is a contractual relationship directly between the supplier and the customer, and the marketplace does not engage in any of the five control factors.
Applies only where the facilitated supply is an electronic (digital) service.
The marketplace is not the deemed supplier only if both:
- the agreement names the non-resident as the principal supplier (supported by appropriate invoice and receipt); and
- the marketplace does none of the five control factors.
The marketplace is not the deemed supplier only if all three:
- it is disclosed (in the contract, invoice and receipt) that the supply is by a non-registered resident; and
- there is a direct contractual relationship between the supplier and the customer; and
- the marketplace does none of the five control factors.
A payment-collecting, terms-setting platform fails the control factors under either scenario, so it is the deemed supplier and must use the Case 2 shape for its non-resident and non-registered SPs.
flowchart TD
A{"Is SP a VAT-registered resident?"} -->|Yes| B["Deemed-supplier rule does not apply.<br/>Free choice among Cases 1 / 2 / 3<br/>on VAT-agency and operational grounds."]
A -->|"No: non-resident (electronic services only) or non-registered resident"| C{"Is the supplier named as principal<br/>(Scenario B: also disclosed & direct<br/>supplier-to-customer contract) AND does the<br/>platform avoid ALL five control factors:<br/>set terms, determine consideration, collect<br/>payment, handle complaints, offer promotions?"}
C -->|"Yes (exception met)"| D["Marketplace not deemed supplier.<br/>SP still cannot issue standard tax invoices<br/>or self-bill if it is unregistered."]
C -->|"No (platform controls payment/terms)"| E["Platform is the deemed supplier.<br/>Use the Case 2 shape: P is the supplier to C<br/>and reports/remits the service VAT."]
For a payment-collecting, terms-setting platform, any SP that is a non-registered resident, or a non-resident supplying electronic services, puts the platform in the deemed-supplier position, and the Case 2 shape becomes mandatory for those transactions. For VAT-registered resident SPs, Cases 1, 2 and 3 (and their variants 4 to 6) all remain available.
5. The Invora platform model¶
REST paths and JSON field names below follow Invora's gRPC-JSON transcoding contract. Paths come from each RPC's google.api.http annotation; JSON bodies use camelCase field names (proto freeze_immediately becomes freezeImmediately).
5.1 Organisation types¶
Invora has three organisation types: Business, Platform, and Connected Business. A Connected Business is a sub-tenant created and managed by a Platform; it is a fully separate tenant with its own users, invoices, documents and CSID, and its usage rolls up to the parent's billing. P upgrades to Platform through platform enrollment, then creates each SP as a Connected Business.
flowchart TD
P["Platform tenant: P<br/>(own self-party, branches, CSID)"]
P --> SP1["Connected Business: SP #1<br/>(own tenant, self-party, CSID)"]
P --> SP2["Connected Business: SP #2<br/>(own tenant, self-party, CSID)"]
P -. "Customers (C) are parties, not tenants" .-> Parties[(Stored parties / inline buyers)]
5.2 The public invoicing surface¶
Everything in this study is built on one public API: Simple. It exposes three resources that behave identically:
invoicescredit-notesdebit-notes
Each resource has the same eight operations. The table shows the paths for invoices; credit-notes and debit-notes are the same with the resource name swapped in. The full surface is documented in Simple invoicing.
| Operation | Method and path |
|---|---|
| Create | POST /api/v1/simple/invoices |
| Get | GET /api/v1/simple/invoices/{key} |
| Update | PUT /api/v1/simple/invoices/{key} |
| List | POST /api/v1/simple/invoices/list |
| Delete | POST /api/v1/simple/invoices/delete |
| Freeze | POST /api/v1/simple/invoices/{key}/freeze |
| Calculate | POST /api/v1/simple/invoices:calculate |
| GetArtifact | GET /api/v1/simple/invoices/{documentKey}/artifacts/{artifactId} |
Freeze is the step that locks an invoice and submits it to ZATCA: clearance for standard invoices, reporting for simplified ones. Calculate previews totals without persisting; GetArtifact downloads a frozen document's QR code or signed XML.
This one API covers all six cases in this study. You choose the case by setting three fields on the request body, buyer, seller and isSummary, and Invora derives the ZATCA classification from them (see §4.5).
The Simple changes body is built from a small set of business facts and the backend derives everything regulatory from them:
buyer(required,SimpleParty): the recipient of this invoice, as apartyKey(a stored party) orinlinedetails. ItsvatRegistrationNumberselects the ZATCA subtype: present →01standard (clearance); absent →02simplified (reporting). On a self-billed invoice the buyer is the issuing tenant's own self-party. You record yourself as the buyer of the supplier's supply (see §6.2).seller(optional,SimpleParty): the supplier of record, set only when it differs from the issuing tenant. Whether it is present, and how it relates to the issuing tenant, selects normal vs self-billed (Art. 53(2)) vs third-party (Art. 53(3)); see the derivation table in §4.5. Self-billed and third-party require a VAT-registered seller (aninlineseller must carryvatRegistrationNumber).isSummary(optional bool): settrueto aggregate a calendar month of supplies to one customer into a single summary invoice (Art. 53(4)). Combinable with self-billed or third-party.
To express "the issuing tenant is the buyer (or seller)", set that counterparty to the tenant's own self-party, by partyKey (or an inline value matching it). The backend never asks for a type code: the classification is computed on freeze from buyer / seller / isSummary (see the mapping in §4.5).
5.3 Seller identity, branches and ZATCA onboarding¶
- The seller for a tenant is its self-party:
GET/PUT /api/v2/settings/self-party. Multi-establishment sellers use branches (POST /api/v2/branches), each carrying its own VAT number, CRN and CSID. ThebranchIdon an invoice selects the issuing establishment and its numbering chain. - ZATCA onboarding runs per tenant or branch:
POST /api/v1/regulations/zatca/onboarding(streams CSR, compliance CSID, production CSID),GET /api/v1/regulations/zatca/integration-status, and per-branchPOST /api/v2/regulations/{regulationId}/enrollment/branches/{branchId}/begin. The branch's ZATCA settings (environment, VAT number, CRN, address) are held in the typedZatcaConfigon the branch'sRegulationConfig.
Tenant-level ZATCA onboarding (POST /api/v1/regulations/zatca/onboarding) — the -N flag keeps the connection open while the server streams three progress events, one per step. Wait for "isFinal": true before submitting live documents.
curl -N -X POST https://stg-gateway.invora.app/api/v1/regulations/zatca/onboarding \
-H "Authorization: Bearer $SP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "otp": "123456", "environment": "ZATCA_ENVIRONMENT_PHASE2_SIMULATION" }'
{"step": "ZATCA_ONBOARDING_STEP_CSR_GENERATION", "disposition": "ZATCA_DISPOSITION_ISSUED", "isFinal": false}
{"step": "ZATCA_ONBOARDING_STEP_COMPLIANCE_CSID", "disposition": "ZATCA_DISPOSITION_ISSUED", "isFinal": false}
{"step": "ZATCA_ONBOARDING_STEP_PRODUCTION_CSID", "disposition": "ZATCA_DISPOSITION_ISSUED", "isFinal": true}
Check integration status after onboarding completes:
curl -X GET https://stg-gateway.invora.app/api/v1/regulations/zatca/integration-status \
-H "Authorization: Bearer $SP_TOKEN"
{
"onboarded": true,
"environment": "ZATCA_ENVIRONMENT_PHASE2_SIMULATION",
"details": { "isCompliant": true, "productionCsidExpiry": "2027-01-15T00:00:00Z" }
}
Per-branch ZATCA enrollment (POST /api/v2/regulations/{regulationId}/enrollment/branches/{branchId}/begin) — use this instead of the tenant-level endpoint when the SP has multiple branches, each with its own CSID and document counter chain. The stream format matches InitiateOnboarding above.
curl -N -X POST https://stg-gateway.invora.app/api/v2/regulations/zatca/enrollment/branches/branch_riyadh/begin \
-H "Authorization: Bearer $SP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "otp": "654321", "environment": "ZATCA_ENVIRONMENT_PHASE2_SIMULATION" }'
{"stepName": "CsrGeneration", "disposition": "ENROLLMENT_DISPOSITION_ISSUED", "isFinal": false}
{"stepName": "ProductionCsid", "disposition": "ENROLLMENT_DISPOSITION_ISSUED", "isFinal": true}
- Submission status and artefacts for a frozen document use three valid live paths:
- Status read with
GET /api/v2/regulations/documents/{documentKey}(ListDocumentRegulations): status, QR, hash, counter. It omitsregulationIdbecause it returns every regulation entry for the document at once. - Artifact download, one regulation with
GET /api/v2/regulations/{regulationId}/documents/{documentKey}/artifact(GetArtifact): the signed e-invoice and QR. It includesregulationIdbecause it returns the bytes of one specific regulation. - All artifacts with
GET /api/v2/regulations/documents/{documentKey}/artifacts(ListDocumentArtifacts): downloads every artifact in one call.
Status read for a frozen invoice:
curl -X GET https://stg-gateway.invora.app/api/v2/regulations/documents/inv_sp1_001 \
-H "Authorization: Bearer $SP_TOKEN"
{
"items": [
{
"regulationId": "zatca",
"submissionStatus": "REGULATION_SUBMISSION_STATUS_ACCEPTED",
"artifacts": [
{ "artifactId": "signed-xml" },
{ "artifactId": "qr-code" }
]
}
]
}
Artifact download (signed XML or QR) for one regulation:
curl -X GET "https://stg-gateway.invora.app/api/v2/regulations/zatca/documents/inv_sp1_001/artifact?artifactId=signed-xml" \
-H "Authorization: Bearer $SP_TOKEN"
{
"artifact": "PD94bWwgdmVyc2lvbj0i...",
"contentType": "application/xml",
"artifactHash": "9f2c1a44..."
}
All artifacts at once (omit artifactId to get every artifact across all active regulations):
curl -X GET https://stg-gateway.invora.app/api/v2/regulations/documents/inv_sp1_001/artifacts \
-H "Authorization: Bearer $SP_TOKEN"
{
"items": [
{
"regulationId": "zatca",
"artifactId": "signed-xml",
"contentType": "application/xml",
"artifact": "PD94bWwgdmVyc2lvbj0i...",
"artifactHash": "9f2c1a44..."
},
{
"regulationId": "zatca",
"artifactId": "qr-code",
"contentType": "image/png",
"artifact": "iVBORw0KGgoAAAANSUhEUg...",
"artifactHash": "a3f72b11..."
}
]
}
5.4 Onboarding an SP as a Connected Business¶
This is the shared first step of Cases 1 and 3.
# P (Platform) creates the SP sub-tenant. The create body is name + adminEmail only.
curl -X POST https://stg-gateway.invora.app/api/v2/identity/connected-businesses \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Speedy Services LLC",
"adminEmail": "admin@speedy.example"
}'
{
"connectedBusiness": {
"tenantId": "tnt_sp1",
"parentTenantId": "tnt_p",
"status": "CONNECTED_BUSINESS_STATUS_PROVISIONING"
},
"clientId": "…",
"clientSecret": "…"
}
M2M credentials for the SP tenant; returned once — store securely.
Set the SP's tax identity inside the SP tenant after creation using the SP's M2M token ($SP_TOKEN):
# 1. Set the SP's legal identity (self-party)
curl -X PUT https://stg-gateway.invora.app/api/v2/settings/self-party \
-H "Authorization: Bearer $SP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mask": "myInfo.party",
"myInfo": {
"party": {
"partyName": [{"name": {"value": "Speedy Services LLC"}}],
"postalAddress": {
"streetName": {"value": "Prince Mohammed Road"},
"cityName": {"value": "Riyadh"},
"postalZone": {"value": "12211"},
"country": {"identificationCode": {"value": "SA"}}
},
"partyTaxScheme": [{"companyId": {"value": "300000000000099"}, "taxScheme": {"id": {"value": "VAT"}}}],
"partyLegalEntity": [{"registrationName": {"value": "Speedy Services LLC"}, "companyId": {"value": "7009876543"}}]
}
}
}'
# 2. Create a branch (one per establishment / CSID)
curl -X POST https://stg-gateway.invora.app/api/v2/branches \
-H "Authorization: Bearer $SP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"changes": {
"name": "Riyadh Main Branch",
"documentPrefix": "SP-RUH-"
}
}'
{ "details": { "key": "branch_riyadh", "name": "Riyadh Main Branch", "documentPrefix": "SP-RUH-" } }
Then run ZATCA onboarding (see §5.3) so the SP receives its own production CSID. P drives these calls machine-to-machine using the SP's clientId/clientSecret (or the platform Impersonation service).
5.5 Producing the platform-fee invoice¶
The platform-fee invoice (Cases 1 and 3, Invoice 2) is a per-transaction P→SP standard tax invoice. There are two ways to produce it:
- As a per-transaction e-invoice on the Simple surface (
/api/v1/simple/invoices). Fits a genuinely per-transaction fee with one tax invoice per service event. - Through the Billing module (
/api/billing/v2/*): meter each transaction as a usage event against the SP's subscription (POST /api/billing/v2/events, where the event'scodematches a billable-metric on the SP's plan), let the plan/subscription aggregate and generate the invoice, which Invora converts into a ZATCA e-invoice and submits it. Fits aggregated or periodic fees, or when you want dunning, wallets, and payment collection.
curl -X POST https://stg-gateway.invora.app/api/billing/v2/events \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"transactionId": "txn-sp1-20260612-001",
"externalSubscriptionId": "sub_sp1",
"code": "platform_transaction",
"timestamp": "2026-06-12T09:00:00Z",
"properties": { "invoiceKey": "inv_sp1_001" }
}'
The case flows use option 1, since the fee here is per-transaction. See §9 for the trade-off.
6. The cases¶
Each case below gives the party tables for both invoices, the regulatory basis, the deemed-supplier verdict, a REST flow, and a pros/cons table split into regulation-side and Invora-side. Flows assume P and the relevant SP are already onboarded to ZATCA Phase 2.
6.1 Case 1: SP is the supplier and self-issues; P bills SP a fee¶
SP supplies the service to C and issues its own invoice (disclosed-agent structure). P supplies an agency service to SP and issues a separate fee invoice.
| Seller | Buyer | Issuer | Subtype | |
|---|---|---|---|---|
| Invoice 1, SP's service to C | SP | C | SP (own tenant) | C consumer → simplified 0200000 (reporting); C business → standard 0100000 (clearance) |
| Invoice 2, platform fee | P | SP | P (own tenant) | standard 0100000 (clearance, B2B) |
Regulatory basis
Ordinary invoicing; no third-party or self-billing mechanism. SP is the supplier to C (disclosed agent, §4.2); P's fee is a normal B2B supply.
Deemed-supplier verdict
Available only when SP is a VAT-registered resident. If SP is non-resident or non-registered, it cannot issue a valid KSA tax invoice and the deemed-supplier rule (§4.6) moves the supply onto P, so Case 1 is not available; use Case 2.
REST flow.
Invoice 1 — SP issues the service invoice in its own tenant. (a) C is a consumer → simplified, reporting. For B2B C, add buyer.inline.vatRegistrationNumber → standard invoice, cleared before valid.
curl -X POST https://stg-gateway.invora.app/api/v1/simple/invoices \
-H "Authorization: Bearer $SP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"freezeImmediately": true,
"changes": {
"issueAt": "2026-06-12T09:00:00Z",
"supplyDate": { "year": 2026, "month": 6, "day": 12 },
"currencyCode": "SAR",
"buyer": { "inline": { "legalName": "Walk-in Customer" } },
"lines": [ {
"description": "Home cleaning visit",
"quantity": { "value": { "units": "1", "nanos": 0 }, "unitCode": "HUR" },
"unitPrice": { "units": "200", "nanos": 0 },
"tax": { "taxRate": { "units": "15", "nanos": 0 } }
} ]
}
}'
{
"details": {
"key": "inv_sp1_001",
"editState": "EDIT_STATE_FROZEN",
"calculations": {
"taxExclusiveAmount": { "units": "200", "nanos": 0 },
"taxAmount": { "units": "30", "nanos": 0 }
}
}
}
Read totals at details.calculations. ZATCA status and QR are available via GET /api/v2/regulations/documents/{documentKey} — not inline on the response. B2C reports asynchronously within 24 h.
Invoice 2 — P issues the platform-fee invoice in P's tenant; buyer = SP (B2B standard, clearance).
curl -X POST https://stg-gateway.invora.app/api/v1/simple/invoices \
-H "Authorization: Bearer $P_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"freezeImmediately": true,
"changes": {
"issueAt": "2026-06-12T09:05:00Z",
"supplyDate": { "year": 2026, "month": 6, "day": 12 },
"currencyCode": "SAR",
"buyer": { "inline": {
"legalName": "Speedy Services LLC",
"vatRegistrationNumber": "3XXXXXXXXXXXXX3",
"address": { "buildingNumber": "1234", "streetName": "…", "cityName": "Riyadh", "postalZone": "12345", "countryCode": "SA" }
} },
"lines": [ {
"description": "Platform fee: transaction INV-… (10%)",
"quantity": { "value": { "units": "1", "nanos": 0 }, "unitCode": "C62" },
"unitPrice": { "units": "20", "nanos": 0 },
"tax": { "taxRate": { "units": "15", "nanos": 0 } }
} ]
}
}'
{
"details": {
"key": "inv_p_fee_001",
"editState": "EDIT_STATE_FROZEN",
"calculations": {
"taxExclusiveAmount": { "units": "20", "nanos": 0 },
"taxAmount": { "units": "3", "nanos": 0 }
}
}
}
Standard tax invoice — cleared before it is valid to hand to SP.
Pros and cons.
| Pros | Cons | |
|---|---|---|
| Regulation-side | • Simplest VAT position: each party invoices its own supply. • No Authority pre-approval for third-party or self-billing. • SP carries its own output tax; P carries only fee VAT. • One supplier per invoice keeps the audit trail clean. |
• Requires every SP to be a VAT-registered resident and a working ZATCA issuer. • Fails for unregistered or non-resident SPs. • Disclosed agency must be genuine (contracts and branding show SP as the seller to C). |
| Invora-side | • Fully buildable on the Simple API today (lowest integration effort): both invoices are standard invoices where the seller is the issuing tenant. • Each SP is an isolated Connected Business with its own CSID and numbering. |
• P onboards every SP to ZATCA and operates issuance inside each SP tenant. • Many tenants to provision and monitor. • Two freezeImmediately calls in two tenants per transaction. |
6.2 Case 2: P is the supplier to C; P self-bills SP for the service¶
P is the supplier to C (undisclosed-agent / commissionaire structure, or deemed supplier by law). SP supplies its service to P, and because P is the buyer of that supply, P issues the self-billed invoice on SP's behalf.
| Seller | Buyer | Issuer | Subtype | |
|---|---|---|---|---|
| Invoice 1, P's resold service to C | P | C | P (own tenant) | C consumer → simplified 0200000 (reporting); C business → standard 0100000 (clearance) |
| Invoice 2, SP's service supply to P (self-billed) | SP | P | P (the buyer, self-billing) | standard self-billed 0100001 (clearance, B2B only) |
flowchart LR
SP["Service Provider (SP)"] -- "Invoice 2: SP→P<br/>(self-billed, issued by P)" --> P["Platform (P)"]
P -- "Invoice 1: P→C<br/>(issued by P)" --> C["Customer (C)"]
Invoice 2 is not the platform fee
Invoice 2 here is not the platform fee. It is SP's supply to P. In the commissionaire structure the platform fee is P's retained margin: (Invoice 1 value, P→C) minus (Invoice 2 value, SP→P). If you also want an explicit fee invoice P→SP, that is an additional standard invoice as in Case 1, but do not double-count the same economics. Confirm the intended expression with the finance and legal team (§11).
Regulatory basis
P as supplier to C is the undisclosed agent treated as principal (§4.2), or the deemed supplier under Art. 47(3) when SP is non-resident or non-registered (§4.6). The SP→P self-billed invoice relies on VAT IR Art. 53(2) (§4.4) and is valid only when both SP and P are VAT-registered, a prior self-billing agreement exists, the invoice states it is self-billed, and SP certifies it will not also invoice P.
Deemed-supplier verdict
This is the shape the law requires when P is a deemed supplier (non-resident or non-registered SP and P controls payment or terms). Caveat: if SP is non-registered, the self-billed Invoice 2 is not available (Art. 53(2) needs both parties registered). In that sub-case there is no SP→P tax invoice; SP's supply to P is out of scope (unregistered resident) or a reverse-charge import of services that P self-accounts for (non-resident SP). Only Invoice 1 (P→C) is a ZATCA e-invoice.
REST flow.
Invoice 1 — P is the supplier to C (P's tenant, Simple surface). B2C → simplified; add buyer.inline.vatRegistrationNumber for B2B.
curl -X POST https://stg-gateway.invora.app/api/v1/simple/invoices \
-H "Authorization: Bearer $P_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"freezeImmediately": true,
"changes": {
"issueAt": "2026-06-12T09:00:00Z",
"supplyDate": { "year": 2026, "month": 6, "day": 12 },
"currencyCode": "SAR",
"buyer": { "inline": { "legalName": "Walk-in Customer" } },
"lines": [ {
"description": "Home cleaning visit",
"quantity": { "value": { "units": "1", "nanos": 0 }, "unitCode": "HUR" },
"unitPrice": { "units": "200", "nanos": 0 },
"tax": { "taxRate": { "units": "15", "nanos": 0 } }
} ]
}
}'
Invoice 2 — P self-bills SP for SP's service (issued in P's tenant). seller = SP (supplier of record); buyer = P's own self-party. Because the issuing tenant (P) is the buyer, the backend derives the self-billed flag (ZATCA code position 7). SP must be VAT-registered (B2B).
curl -X POST https://stg-gateway.invora.app/api/v1/simple/invoices \
-H "Authorization: Bearer $P_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"freezeImmediately": true,
"changes": {
"issueAt": "2026-06-12T09:05:00Z",
"supplyDate": { "year": 2026, "month": 6, "day": 12 },
"currencyCode": "SAR",
"seller": { "inline": {
"legalName": "Speedy Services LLC",
"vatRegistrationNumber": "3XXXXXXXXXXXXX3",
"address": { "buildingNumber": "1234", "streetName": "…", "cityName": "Riyadh", "citySubdivisionName": "Al Olaya", "postalZone": "12345", "countryCode": "SA" }
} },
"buyer": { "partyKey": "party_p_self" },
"lines": [ {
"description": "Cleaning service supplied to platform: transaction INV-…",
"quantity": { "value": { "units": "1", "nanos": 0 }, "unitCode": "HUR" },
"unitPrice": { "units": "180", "nanos": 0 },
"tax": { "taxRate": { "units": "15", "nanos": 0 } }
} ]
}
}'
Standard self-billed tax invoice; seller of record = SP. P deducts input tax; P accounts for output tax on the full Invoice 1 to C.
The backend derives the self-billed subtype
You never set a type code. By putting seller = SP and buyer = P's own self-party, the issuing tenant (P) is the buyer, so Invora classifies Invoice 2 as self-billed (VAT IR Art. 53(2), position 7 of the ZATCA code) and keeps SP as the seller of record. Art. 53(2) still requires both parties registered, a prior self-billing agreement, the self-billed statement on the invoice, and SP's non-duplication certification (managed out of band).
For Cases 2 and 3, the legal supplier is fixed by the seller party on the document (SP on both invoices here). Which tenant or CSID signs the document, and how the numbering chain is attributed, is an Invora backend behaviour, an open item; see §11.6.
Pros and cons.
| Pros | Cons | |
|---|---|---|
| Regulation-side | • The only lawful shape when P is a deemed supplier (non-resident or non-registered SPs). • One supplier of record to C (P) simplifies customer-side disputes. • Self-billing removes the need for many small SPs to operate ZATCA. |
• P carries the full service output VAT, not just fee VAT (larger VAT cash-flow and exposure). • Self-billing has four conditions (Art. 53(2)): prior agreement, both registered, no duplication, stated self-billed. • It is invalid the moment an SP is unregistered. • B2B only, and barred for exports. • Misclassifying a disclosed arrangement as commissionaire is a VAT risk. |
| Invora-side | • Both invoices build on the Simple API: Invoice 1 (P→C) is a normal invoice; Invoice 2 is self-billed via seller = SP + buyer = P's self-party, with the subtype derived for you.• P issues everything from P's tenant. • Fewer issuing tenants. |
• The self-billing agreement lifecycle is managed out of band. • Cross-tenant signing and counter behaviour needs validation. |
6.3 Case 3: SP is the supplier, P issues on SP's behalf (third-party)¶
Same VAT position as Case 1 (SP is the supplier to C), but P issues Invoice 1 on SP's behalf through third-party invoicing. SP stays the supplier of record and keeps the data and output-tax responsibility.
| Seller | Buyer | Issuer | Subtype | |
|---|---|---|---|---|
| Invoice 1, SP's service to C | SP | C | P (third party) | C consumer → simplified third-party 0210000 (reporting); C business → standard third-party 0110000 (clearance) |
| Invoice 2, platform fee | P | SP | P (own tenant) | standard 0100000 (clearance, B2B) |
flowchart LR
SP["Service Provider (SP)"] -- "Invoice 1: SP→C<br/>(third-party, issued by P)" --> C["Customer (C)"]
P["Platform (P)"] -- "Invoice 2: P→SP<br/>(platform fee, issued by P)" --> SP
Regulatory basis
VAT IR Art. 53(3) (§4.3): a taxable person may issue tax invoices through a third party, and the supplier remains responsible for accuracy and for reporting output tax. The invoice carries the third-party flag (position 3 of the ZATCA code = 1). ZATCA's third-party sample keeps the seller as SP, not P.
Deemed-supplier verdict
Available only when SP is a VAT-registered resident, the same rule as Case 1. If SP is non-resident or non-registered, the deemed-supplier rule forces the Case 2 shape; third-party issuance cannot make an invalid supplier into a valid one.
REST flow.
Invoice 1 — P issues SP's service invoice to C as a third party (P's tenant). seller = SP (supplier of record); buyer = C. The issuing tenant (P) is neither buyer nor seller → backend derives the third-party flag (ZATCA code position 3). C consumer → simplified third-party; add buyer.inline.vatRegistrationNumber for B2B standard.
curl -X POST https://stg-gateway.invora.app/api/v1/simple/invoices \
-H "Authorization: Bearer $P_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"freezeImmediately": true,
"changes": {
"issueAt": "2026-06-12T09:00:00Z",
"supplyDate": { "year": 2026, "month": 6, "day": 12 },
"currencyCode": "SAR",
"seller": { "partyKey": "party_sp1" },
"buyer": { "inline": {
"legalName": "Walk-in Customer",
"address": { "buildingNumber": "55", "streetName": "…", "cityName": "Riyadh", "citySubdivisionName": "Al Olaya", "postalZone": "12345", "countryCode": "SA" }
} },
"lines": [ {
"description": "Home cleaning visit",
"quantity": { "value": { "units": "1", "nanos": 0 }, "unitCode": "HUR" },
"unitPrice": { "units": "200", "nanos": 0 },
"tax": { "taxRate": { "units": "15", "nanos": 0 } }
} ]
}
}'
SP stays the seller of record; P operates issuance.
Invoice 2 — platform fee (P → SP): P's tenant, Simple surface; buyer = SP; no seller (P is the supplier — normal standard invoice, identical to Case 1's Invoice 2).
curl -X POST https://stg-gateway.invora.app/api/v1/simple/invoices \
-H "Authorization: Bearer $P_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"freezeImmediately": true,
"changes": {
"issueAt": "2026-06-12T09:05:00Z",
"supplyDate": { "year": 2026, "month": 6, "day": 12 },
"currencyCode": "SAR",
"buyer": { "inline": {
"legalName": "Speedy Services LLC",
"vatRegistrationNumber": "3XXXXXXXXXXXXX3",
"address": { "buildingNumber": "1234", "streetName": "…", "cityName": "Riyadh", "postalZone": "12345", "countryCode": "SA" }
} },
"lines": [ {
"description": "Platform fee: transaction INV-… (10%)",
"quantity": { "value": { "units": "1", "nanos": 0 }, "unitCode": "C62" },
"unitPrice": { "units": "20", "nanos": 0 },
"tax": { "taxRate": { "units": "15", "nanos": 0 } }
} ]
}
}'
Standard tax invoice (no seller ⇒ normal), cleared before it is valid to hand to SP.
The backend derives the third-party subtype
You never set a type code. By putting seller = SP while the issuing tenant (P) is neither buyer nor seller, Invora classifies Invoice 1 as third-party (VAT IR Art. 53(3), position 3 of the ZATCA code) and keeps SP as the seller of record. Third-party issuance is subject to the Authority's approval and SP must be VAT-registered. Invoice 2 (the platform fee, P→SP) omits seller, so it is a normal standard invoice.
Third-party issuance means P operates the issuance while SP is the supplier of record. The two options are: (a) P drives the SP's tenant through the SP's M2M credentials or the Impersonation service, so SP's production CSID signs and SP's numbering chain advances (keeps the chain with the supplier); or (b) P issues from P's own solution as a registered third party. This signing/CSID choice is an open item (see §11.6).
Pros and cons.
| Pros | Cons | |
|---|---|---|
| Regulation-side | • Same clean VAT position as Case 1 (SP is supplier; P carries only fee VAT), with the issuance burden centralised on P. • Sanctioned by Art. 53(3). • SP stays responsible for accuracy and output tax (clear liability line). • Works for both B2C (third-party simplified) and B2B (third-party standard). |
• Requires SP to be a VAT-registered resident (no help for unregistered or non-resident SPs). • Third-party issuance needs Authority approval and correct flagging. • SP remains liable for errors P makes. • Disclosed agency must be genuine. |
| Invora-side | • P centralises the integration (one issuing pipeline) while keeping SP as supplier: Invoice 1 is third-party via seller = SP, Invoice 2 (the fee) omits seller. Both build on the Simple API.• The best operational ergonomics of the three for a platform that wants control without becoming the supplier. |
• Still onboards each SP to ZATCA (SP needs a CSID as supplier of record). • Cross-tenant third-party signing / CSID attribution is an open item (see §10.6). |
6.4 Case 4: P is the supplier to C; SP invoices P normally (principal without self-billing)¶
Same supplier position as Case 2 (P is the supplier to C), but the inter-party invoice is an ordinary tax invoice issued by SP, not a self-billed invoice issued by P. This is the default reseller/principal setup when SP is a VAT-registered issuer.
| Seller | Buyer | Issuer | Subtype | |
|---|---|---|---|---|
| Invoice 1, P's resold service to C | P | C | P (own tenant) | C consumer → simplified 0200000 (reporting); C business → standard 0100000 (clearance) |
| Invoice 2, SP's service supply to P | SP | P | SP (own tenant) | standard 0100000 (clearance, B2B) |
Invoice 2 is not the platform fee
Invoice 2 here is SP's supply to P, the same economics as Case 2's Invoice 2, but issued by SP under ordinary invoicing rather than self-billed by P. The platform fee remains P's retained margin: (Invoice 1, P→C) minus (Invoice 2, SP→P).
Regulatory basis
P as supplier to C is the undisclosed agent treated as principal, or the deemed supplier under Art. 47(3) (§4.2, §4.6). Invoice 2 is an ordinary tax invoice from SP to P (VAT IR Art. 53(1)). Unlike Case 2, no self-billing agreement or Authority approval is needed, but SP must be VAT-registered to issue it.
Deemed-supplier verdict
This is the principal shape when SP is a VAT-registered resident. If SP is non-registered or non-resident it cannot issue Invoice 2, and the deemed-supplier rule still places the supply on P; the SP→P supply then becomes out of scope or a reverse-charge import, which is the Case 2 fallback. Choose Case 4 over Case 2 when SPs are registered and willing to invoice P; choose Case 2 when they are not.
REST flow.
Invoice 1 — P is the supplier to C (P's tenant, Simple surface; identical to Case 2's Invoice 1). B2C → simplified; add buyer.inline.vatRegistrationNumber for B2B.
curl -X POST https://stg-gateway.invora.app/api/v1/simple/invoices \
-H "Authorization: Bearer $P_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"freezeImmediately": true,
"changes": {
"issueAt": "2026-06-12T09:00:00Z",
"supplyDate": { "year": 2026, "month": 6, "day": 12 },
"currencyCode": "SAR",
"buyer": { "inline": { "legalName": "Walk-in Customer" } },
"lines": [ {
"description": "Home cleaning visit",
"quantity": { "value": { "units": "1", "nanos": 0 }, "unitCode": "HUR" },
"unitPrice": { "units": "200", "nanos": 0 },
"tax": { "taxRate": { "units": "15", "nanos": 0 } }
} ]
}
}'
Invoice 2 — SP issues its own tax invoice to P (SP's tenant, Simple surface; buyer = P, standard clearance).
curl -X POST https://stg-gateway.invora.app/api/v1/simple/invoices \
-H "Authorization: Bearer $SP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"freezeImmediately": true,
"changes": {
"issueAt": "2026-06-12T09:05:00Z",
"supplyDate": { "year": 2026, "month": 6, "day": 12 },
"currencyCode": "SAR",
"buyer": { "inline": {
"legalName": "The Platform LLC",
"vatRegistrationNumber": "3XXXXXXXXXXXXX3",
"address": { "buildingNumber": "100", "streetName": "…", "cityName": "Riyadh", "postalZone": "12345", "countryCode": "SA" }
} },
"lines": [ {
"description": "Cleaning service supplied to platform: transaction INV-…",
"quantity": { "value": { "units": "1", "nanos": 0 }, "unitCode": "HUR" },
"unitPrice": { "units": "180", "nanos": 0 },
"tax": { "taxRate": { "units": "15", "nanos": 0 } }
} ]
}
}'
Standard tax invoice, cleared. P deducts the input tax; P accounts for output tax on the full Invoice 1 to C.
Pros and cons.
| Pros | Cons | |
|---|---|---|
| Regulation-side | • Simplest principal model: ordinary invoicing on both invoices, no self-billing agreement or Authority approval. • SP carries its own output tax on the SP→P supply; P deducts it as input tax. • One supplier per invoice. |
• P still carries the full service output VAT to C (principal exposure). • Requires SP to be a VAT-registered resident. • Misclassifying a disclosed arrangement as principal is a VAT risk, the same caveat as Case 2. |
| Invora-side | • Fully buildable on the Simple API today: both invoices are standard invoices where the seller is the issuing tenant. • SP issues from its own tenant. |
• Each SP must be ZATCA-onboarded as an issuer (same as Cases 1 and 3). • Two freezeImmediately calls in two tenants per transaction.• P depends on SPs to issue Invoice 2 on time. |
6.5 Case 5: Disclosed agent; SP self-bills P for the commission¶
Same supplier position as Cases 1 and 3 (SP is the supplier to C), but the platform fee is self-billed by SP instead of issued by P. P's commission is itself a taxable supply to SP, so SP, as the buyer of that commission, may self-bill P under Art. 53(2).
| Seller | Buyer | Issuer | Subtype | |
|---|---|---|---|---|
| Invoice 1, SP's service to C | SP | C | SP (Case 1) or P third-party (Case 3) | C consumer → simplified; C business → standard |
| Invoice 2, platform commission | P | SP | SP (the buyer, self-billing) | standard self-billed 0100001 (clearance, B2B only) |
flowchart LR
SP["Service Provider (SP)"] -- "Invoice 1: SP→C<br/>(issued by SP, or by P third-party)" --> C["Customer (C)"]
P["Platform (P)"] -- "Invoice 2: P→SP commission<br/>(self-billed, issued by SP)" --> SP
Regulatory basis
Disclosed agent: SP is the supplier to C (§4.2). Invoice 1 follows Case 1 or Case 3. Invoice 2 applies VAT IR Art. 53(2) (§4.4) to the commission supply: SP (the buyer of the commission) issues the self-billed invoice on P's behalf. The same four conditions apply: prior agreement, both parties VAT-registered, the invoice states it is self-billed, and P certifies it will not also issue its own fee invoice.
Deemed-supplier verdict
Available only when SP is a VAT-registered resident (self-billing needs both parties registered, and SP must be a valid supplier of record for Invoice 1). Non-registered or non-resident SPs fall to Case 2.
REST flow.
Invoice 1 — follows Case 1 (SP issues in its own tenant) or Case 3 (P issues third-party for SP). See those flows.
Invoice 2 — SP self-bills P for the commission (issued in SP's tenant). seller = P (supplier of the commission); buyer = SP's own self-party. Because the issuing tenant (SP) is the buyer, the backend derives the self-billed flag (ZATCA code position 7). P must be VAT-registered (B2B).
curl -X POST https://stg-gateway.invora.app/api/v1/simple/invoices \
-H "Authorization: Bearer $SP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"freezeImmediately": true,
"changes": {
"issueAt": "2026-06-12T09:05:00Z",
"supplyDate": { "year": 2026, "month": 6, "day": 12 },
"currencyCode": "SAR",
"seller": { "partyKey": "party_p" },
"buyer": { "partyKey": "party_sp1_self" },
"lines": [ {
"description": "Platform commission: transaction INV-… (10%)",
"quantity": { "value": { "units": "1", "nanos": 0 }, "unitCode": "C62" },
"unitPrice": { "units": "20", "nanos": 0 },
"tax": { "taxRate": { "units": "15", "nanos": 0 } }
} ]
}
}'
{
"details": { "key": "inv_sp1_selfbill_commission_001", "editState": "EDIT_STATE_FROZEN" }
}
Standard self-billed tax invoice; seller of record = P.
The backend derives the self-billed subtype
You never set a type code. By putting seller = P and buyer = SP's own self-party, the issuing tenant (SP) is the buyer, so Invora classifies Invoice 2 as self-billed (VAT IR Art. 53(2), position 7 of the ZATCA code) and keeps P as the seller of record. The same four Art. 53(2) conditions apply (both registered, prior agreement, self-billed statement, P's non-duplication certification). Invoice 1 follows Case 1 (a normal Simple invoice) or Case 3 (third-party via seller = SP).
Pros and cons.
| Pros | Cons | |
|---|---|---|
| Regulation-side | • Clean disclosed-agent VAT position (SP is supplier; P carries only fee VAT). • P avoids issuing many small fee invoices because SP self-bills them. • Useful at high SP volume. |
• Adds the four Art. 53(2) self-billing conditions to the fee invoice: prior agreement, both registered, no duplication, stated self-billed, plus Authority approval. • B2B only. • Both Invoice 1 and the self-billed fee require SP to be onboarded. |
| Invora-side | • Both invoices build on the Simple API: Invoice 1 per Case 1/3, and the commission invoice self-billed via seller = P + buyer = SP's self-party.• P operates fewer issuing flows because SP self-bills the fee. |
• Highest SP-side complexity: SP runs both the service invoice and the self-billed fee invoice. • Cross-tenant signing and counter behaviour needs validation, the same caveat as Case 2. |
6.6 Case 6: Periodic summary fee invoice¶
An aggregation variant that layers on the fee invoice of Case 1, 3, or 4. Instead of one fee invoice per transaction, P issues a single monthly summary tax invoice to each SP, covering all that month's fees. Invoice 1 stays per transaction in the parent case.
| Seller | Buyer | Issuer | Subtype | |
|---|---|---|---|---|
| Invoice 1, per transaction | per parent case (Case 1/3/4) | C | per parent case | per parent case |
| Invoice 2, monthly summary of platform fees | P | SP | P (own tenant) | standard summary 0100010 (clearance, B2B) |
Regulatory basis
VAT IR Art. 53(4): a taxable person may issue a summary tax invoice that includes more than one separate supply to the same customer within a period not exceeding one calendar month, issued no later than the fifteenth day of the month following. The document carries the summary flag (position 6 of the ZATCA code = 1; sample value 0100010). This applies to the P→SP fee invoice, where SP is the same customer across many transactions.
Deemed-supplier verdict
Independent of the deemed-supplier rule; it changes only how the fee invoice is aggregated. It composes with whichever parent case is lawful for the SP (Case 1 or 3 for the disclosed model, Case 4 for the principal model). It does not apply to the self-billed fee invoice of Case 5 without a separate self-billing arrangement covering summary invoices.
REST flow. There are two public paths: a native summary tax invoice on the Simple API (Option A), and metered aggregation through the Billing module (Option B).
Option A — native summary invoice on the Simple API: one invoice carrying the month's fee lines for one SP, with isSummary: true. The backend derives the summary flag (ZATCA code position 6). buyer = SP; no seller (P is the supplier of the fees).
curl -X POST https://stg-gateway.invora.app/api/v1/simple/invoices \
-H "Authorization: Bearer $P_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"freezeImmediately": true,
"changes": {
"issueAt": "2026-07-05T09:00:00Z",
"supplyDate": { "year": 2026, "month": 6, "day": 1 },
"supplyEndDate": { "year": 2026, "month": 6, "day": 30 },
"currencyCode": "SAR",
"isSummary": true,
"buyer": { "inline": {
"legalName": "Speedy Services LLC",
"vatRegistrationNumber": "3XXXXXXXXXXXXX3",
"address": { "buildingNumber": "1234", "streetName": "…", "cityName": "Riyadh", "citySubdivisionName": "Al Olaya", "postalZone": "12345", "countryCode": "SA" }
} },
"lines": [
{
"description": "Platform fee: transaction INV-1001",
"quantity": { "value": { "units": "1", "nanos": 0 }, "unitCode": "C62" },
"unitPrice": { "units": "20", "nanos": 0 },
"tax": { "taxRate": { "units": "15", "nanos": 0 } }
},
{
"description": "Platform fee: transaction INV-1002",
"quantity": { "value": { "units": "1", "nanos": 0 }, "unitCode": "C62" },
"unitPrice": { "units": "15", "nanos": 0 },
"tax": { "taxRate": { "units": "15", "nanos": 0 } }
}
]
}
}'
One standard summary tax invoice covering the month's fees for this SP. Each line should reference its source transaction for audit.
Option B — Billing module: meter each transaction's fee as a usage event (one call per transaction). The event attaches to a subscription; code must match a billable-metric configured on the SP's plan.
curl -X POST https://stg-gateway.invora.app/api/billing/v2/events \
-H "Authorization: Bearer $P_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"transactionId": "txn-INV-1001",
"code": "platform_fee",
"externalSubscriptionId": "sub_sp1",
"timestamp": "2026-06-12T09:05:00Z",
"properties": { "amount": "20.00", "documentRef": "INV-1001" }
}'
{
"event": { "transactionId": "txn-INV-1001", "code": "platform_fee", "status": "EVENT_STATUS_RECEIVED" }
}
At cycle close, Invora aggregates the events into one invoice and converts it to a ZATCA e-invoice.
Two ways to aggregate fees
Option A is best when you assemble the fee lines yourself and want one summary tax invoice per SP per month. Option B (Billing module) is best when you also want metered aggregation, plans, wallets, dunning, and payment collection. Invora aggregates the events into one invoice and converts it to a ZATCA e-invoice. Both carry the summary semantics; Option A sets the ZATCA summary flag directly via isSummary.
Pros and cons.
| Pros | Cons | |
|---|---|---|
| Regulation-side | • One fee invoice per SP per month instead of thousands; fewer ZATCA submissions. • Explicitly allowed by Art. 53(4). |
• Constrained to one calendar month and the same customer, and must be issued by the fifteenth of the following month. • Each fee line should reference its source transaction for audit. • Does not change the VAT position, only the document count. |
| Invora-side | • Native summary invoice on the Simple API via isSummary: true (Option A), or metered aggregation through the Billing module (Option B) with automatic ZATCA e-invoice conversion.• Lowest document volume of any fee option. |
• Option B requires exact reconciliation between metered events and invoice lines. • Option A requires you to assemble the month's fee lines and reference each source transaction. |
7. Permutation matrix¶
Which cases are lawful for each permutation. P is always a VAT-registered resident. Columns are grouped by VAT position: Cases 1 and 3 keep SP as the supplier of record (disclosed agent); Case 2 makes P the supplier (undisclosed / deemed).
| SP status | C type | Case 1 (SP self-issues) | Case 3 (P third-party for SP) | Case 2 (P is supplier, self-bills SP) |
|---|---|---|---|---|
| VAT-registered resident | Consumer (B2C) | Available: simplified, reporting | Available: third-party simplified 0210000 |
Available: P→C simplified; SP→P self-billed 0100001 |
| VAT-registered resident | Business (B2B) | Available: standard, clearance | Available: third-party standard 0110000 |
Available: P→C standard; SP→P self-billed |
| Non-registered resident | Consumer / Business | Not available: SP cannot issue a tax invoice | Not available: SP is not a valid supplier of record | Required by Art. 47(3): P is deemed supplier. No self-billed SP→P invoice (Art. 53(2) needs both registered); SP's supply to P is out of scope |
| Non-resident | Consumer / Business | Not available: non-resident not required or able to issue a KSA tax invoice | Not available: same | For electronic services: required by Art. 47(3): P is deemed supplier; SP→P supply is a reverse-charge import P self-accounts for; no self-billed e-invoice. For non-electronic (physically-performed) services, Art. 47(3) Scenario A does not apply; the supply is outside the deemed-supplier rule (treat per ordinary non-resident/reverse-charge VAT rules) |
When all SPs are VAT-registered residents, the decision is a business choice among the cases. With unregistered or non-resident SPs, Art. 47(3) forces the Case 2 shape for those transactions, and the SP→P supply is handled as out of scope or reverse charge rather than a self-billed invoice. A platform with a mixed SP base typically runs Case 1 or 3 for registered SPs and Case 2 for the rest; Invora supports both concurrently across tenants.
Cases 4 to 6 attach to the registered-resident rows:
- Case 4 (principal, SP invoices P) and Case 5 (self-billed commission) need a VAT-registered resident SP.
- Case 6 (summary fee) layers on the fee invoice of Case 1, 3, or 4 for any registered SP.
8. Decision framework¶
This section compares the cases and lists the factors that point to each one. It does not pick a case; the classification, especially disclosed vs undisclosed agency, is a legal and tax determination.
8.1 Comparison¶
Each case is summarised by the attributes that usually drive the choice.
| Case | Supplier to C | Inter-party (fee) invoice | Needs registered SP | P's VAT exposure | Invora Simple API | Best when |
|---|---|---|---|---|---|---|
| 1 | SP | P to SP, issued by P | Yes | Fee only | Both invoices normal (no seller) |
SPs registered; lowest integration effort |
| 2 | P | SP to P, self-billed by P | No (covers unregistered) | Full service | Invoice 1 normal; Invoice 2 self-billed (seller = SP, buyer = P self-party) |
deemed-supplier, or unregistered/non-resident SPs |
| 3 | SP | P to SP, issued by P | Yes | Fee only | Invoice 1 third-party (seller = SP); Invoice 2 normal |
SP is supplier but P centralises issuance |
| 4 | P | SP to P, issued by SP | Yes | Full service | Both invoices normal (no seller) |
principal model with registered SPs |
| 5 | SP | P to SP, self-billed by SP | Yes | Fee only | Invoice 1 per Case 1/3; commission self-billed (seller = P, buyer = SP self-party) |
disclosed model; push fee issuance to SP at volume |
| 6 | per parent case | P to SP, monthly summary | per parent case | per parent case | isSummary: true on the fee invoice, or Billing events |
cut fee document volume |
8.2 Which case fits which situation¶
- Case 1 fits when SPs are all VAT-registered residents, willing to act as their own supplier of record, and you want the lowest classification risk and lowest integration effort (Simple surface only).
- Case 3 fits when SPs are VAT-registered residents but you want to centralise the issuing pipeline on P while keeping SP as the legal supplier: the disclosed-agent position of Case 1 with operational control closer to Case 2.
- Case 2 is required when you onboard non-resident or non-registered SPs (the deemed-supplier rule makes it mandatory), or when P genuinely acts as an undisclosed principal reselling the service to C. It carries the largest VAT footprint and the strict self-billing conditions.
- Case 4 fits the principal model when SPs are registered and will invoice P themselves: like Case 2 without the self-billing agreement.
- Case 5 fits a disclosed model at high SP volume, pushing fee-invoice issuance onto SPs through self-billing.
- Case 6 fits any per-transaction fee invoice you want to collapse into one monthly invoice per SP.
- A hybrid is the common outcome: Case 1 or 3 for registered-resident SPs, Case 2 for the SPs the deemed-supplier rule captures.
8.3 The two decisions that drive the choice¶
flowchart TD
Q1{"Is the agency relationship disclosed to C?"}
Q1 -->|"Yes: SP is the supplier to C"| Q2{"Who issues Invoice 1?"}
Q1 -->|"No: P is the supplier to C"| Q3{"Who issues the SP-to-P invoice?"}
Q2 -->|"SP issues"| C1["Case 1"]
Q2 -->|"P issues (third-party)"| C3["Case 3"]
Q3 -->|"P self-bills SP"| C2["Case 2"]
Q3 -->|"SP invoices P normally"| C4["Case 4"]
Q1 -.->|"Rule: SP non-resident or non-registered forces Case 2"| G["Art. 47(3) deemed supplier"]
Two refinements layer on top: in the disclosed branch, SP can self-bill P's commission instead of P issuing the fee (Case 5); and any per-transaction P-to-SP fee invoice (Cases 1, 3, 4) can be aggregated into one monthly summary invoice (Case 6).
9. Implementation notes¶
- Platform-fee modelling. The flows issue Invoice 2 as a per-transaction tax invoice. To aggregate fees (monthly statement, wallets, dunning), use the Billing module: meter each event with
POST /api/billing/v2/events, attach a plan or subscription, and Invora generates the billing invoice and converts it into a ZATCA e-invoice. Model each fee with one source of truth, not both. - B2C latency vs B2B blocking. Simplified invoices (consumer C) report asynchronously and are valid immediately, transmitted within 24 hours. Standard invoices (business C, and all P→SP fee invoices) clear synchronously and are valid only after ZATCA returns cleared. Build the customer-facing flow around the async path and the SP-facing and fee flows around the blocking path.
- Numbering and the ZATCA counter chain are per branch and CSID.
ZatcaRegulationMetadata.invoiceCounterValueandpreviousInvoiceHashincrease monotonically per branch. Keep each SP's issuance on that SP's branch chain. This is relevant to the cross-tenant signing question in Cases 2 and 3. - Idempotency. Pass your own
keyon Create (the Simple resources accept it) so retries do not double-issue. For Billing events,transactionIdis the idempotency key. - Read status after freeze. Poll
GET /api/v2/regulations/documents/{documentKey}for the authoritativeZatcaSubmissionStatus(CLEARED,REPORTED,NOT_CLEARED,FAILED: these are shorthand; the JSON enum values are prefixedZATCA_SUBMISSION_STATUS_*, and the full enum also includesNOT_SUBMITTED,NOT_REPORTED,ACCEPTED_WITH_WARNING, andUNSPECIFIED) and fetch the signed e-invoice or QR throughGET /api/v2/regulations/{regulationId}/documents/{documentKey}/artifact.
# Poll submission status for a frozen document
curl -X GET https://stg-gateway.invora.app/api/v2/regulations/documents/inv_sp1_001 \
-H "Authorization: Bearer $SP_TOKEN"
{
"items": [
{
"regulationId": "zatca",
"submissionStatus": "REGULATION_SUBMISSION_STATUS_ACCEPTED",
"artifacts": [{ "artifactId": "signed-xml" }, { "artifactId": "qr-code" }]
}
]
}
# Download the signed XML artifact
curl -X GET "https://stg-gateway.invora.app/api/v2/regulations/zatca/documents/inv_sp1_001/artifact?artifactId=signed-xml" \
-H "Authorization: Bearer $SP_TOKEN"
{ "artifact": "PD94bWwgdmVyc2lvbj0i...", "contentType": "application/xml", "artifactHash": "9f2c1a44..." }
- Credit and debit notes inherit the invoice. A correction is a credit or debit note with the same parties as the document it adjusts, issued on the Simple siblings
POST /api/v1/simple/credit-notesandPOST /api/v1/simple/debit-notes. Each correction body: - requires
sourceDocumentKey, the key of the invoice being adjusted; - requires
reasonCode, a ZATCA reason code such as cancellation, wrong amount, goods returned, or post-sale discount; - carries the same optional
selleras the original, so a self-billed or third-party correction mirrors that invoice and the backend derives the same subtype; - has no summary form:
isSummaryis invoice-only, so correcting a summary fee invoice is an ordinary credit or debit note against it.
The amended VAT IR sets a credit-note issuance deadline of 15 days from the end of the month of the triggering event.
# Issue a credit note correcting invoice inv_sp1_001 (e.g. partial refund)
curl -X POST https://stg-gateway.invora.app/api/v1/simple/credit-notes \
-H "Authorization: Bearer $SP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"freezeImmediately": true,
"changes": {
"sourceDocumentKey": "inv_sp1_001",
"reasonCode": "CANCELLATION",
"issueAt": "2026-06-13T10:00:00Z",
"supplyDate": { "year": 2026, "month": 6, "day": 12 },
"buyer": { "inline": { "legalName": "Walk-in Customer" } },
"lines": [{
"description": "Cancellation of home cleaning visit",
"quantity": { "value": { "units": "1", "nanos": 0 }, "unitCode": "HUR" },
"unitPrice": { "units": "200", "nanos": 0 },
"tax": { "taxRate": { "units": "15", "nanos": 0 } }
}]
}
}'
{
"details": {
"key": "cn_sp1_001",
"editState": "EDIT_STATE_FROZEN",
"sourceDocumentKey": "inv_sp1_001",
"reasonCode": "CANCELLATION"
}
}
Debit notes follow the same shape on POST /api/v1/simple/debit-notes with the same required sourceDocumentKey and reasonCode.
- Billing branding. External artefacts use the Invora Billing brand.
10. Tap payment collection¶
The cases above decide who is the seller of record to C. A separate but linked decision is who collects C's payment — and on Invora that is configured per tenant through the Billing module's payment-provider integrations (Tap, plus Stripe, Adyen, and others). Each case states its own answer inline (the Payment collection note in §6); this section covers the cross-cutting mechanics and the regulatory tie-in that apply across all six.
Payment collection is a deemed-supplier control factor
"Collect payment" is one of the five control factors in the deemed-supplier exception (§2, §4.6). For VAT-registered resident SPs the rule does not apply, so P may collect on an SP's behalf without changing the VAT position. But collecting C's payment into P's own account is evidence that, alongside the other factors, can tip a disclosed-agent arrangement (Case 1/3) toward being treated as an undisclosed principal (Case 2). The seller party on the e-invoice fixes the VAT treatment, not the payment rail — keep the collecting tenant aligned with the seller of record, or use a settlement model that routes funds to the legal supplier.
10.1 How Tap is wired on Invora¶
Tap is one provider on the billing PaymentProvidersService; the flow is provider-agnostic.
-
Configure once per tenant. Each tenant that needs to collect (P, and each SP that collects in its own name) adds its Tap account with
CreateTap(POST /api/billing/v2/integrations/tap, scopeInvora.Billing.PaymentProviders.v2.Modify.CreateTap). Update later withUpdateTap(PUT /api/billing/v2/integrations/tap/{id}). The integration stores that tenant's own Tap API key, so settlement lands in that tenant's Tap merchant account.curl -X POST https://stg-gateway.invora.app/api/billing/v2/integrations/tap \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "apiKey": "sk_test_YOUR_TAP_KEY", "code": "tap_main", "name": "Tap Payments", "successRedirectUrl": "https://your-app.com/payment/success", "supports3ds": true }' -
Link each billing customer to Tap (mandatory before first collection). After
CreateTap, callCustomersService.Update(PUT /api/billing/v2/customers/{id}) withpaymentProvider: "PROVIDER_TYPE_TAP"(the enum name, not the slugtap),paymentProviderCodematching the code above, andproviderCustomer.syncWithProvider: true. This creates the provider-customer record. Omitting this step causesGetPaymentUrlto return400 no_linked_payment_provider.curl -X PUT https://stg-gateway.invora.app/api/billing/v2/customers/BILLING_CUSTOMER_ID \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "paymentProvider": "PROVIDER_TYPE_TAP", "paymentProviderCode": "tap_main", "providerCustomer": { "syncWithProvider": true }, "updateMask": "paymentProvider,paymentProviderCode,providerCustomer" }'See Collecting payments with Tap — step 2 for the full field reference.
-
Collect a customer payment. Generate a hosted payment URL for a finalized invoice under the collecting tenant with
GetPaymentUrl(POST /api/billing/v2/payments/get-payment-url, scopeInvora.Billing.Payments.v2.GetPaymentUrl), then redirect C to it. -
Who holds the integration is who collects. Because each integration carries exactly one tenant's keys, the tenant that issues the payment URL is the tenant whose Tap account receives the funds. "Which Tap account collects" is therefore the same decision as "which tenant runs
GetPaymentUrl". - For sandbox test keys, saved cards, webhooks, and an end-to-end walk-through, see the Collecting payments with Tap guide.
10.2 Centralised collection with payout to SP¶
A platform often wants C to pay through one branded checkout (P's) while the money is owed to SP (Cases 1, 3, 5). Two patterns keep the VAT position and the cash flow aligned:
- Collect in the SP tenant. P drives
GetPaymentUrlmachine-to-machine using the SP's credentials (or the platform Impersonation service, as in §5.4); funds settle directly to SP's Tap account. This is the cleanest VAT alignment — the collecting tenant equals the seller of record. - Collect centrally, then pay out. P collects into P's account and remits SP's share out of band. Operationally simpler, but P now "collects payment" for SP's supply, which adds weight to a deemed-supplier / undisclosed-principal reading — confirm the classification (open question §11.1) and document the agency relationship before relying on it.
Whichever pattern you choose, the e-invoice seller must remain the legal supplier; the payment rail does not change the ZATCA classification.
11. Open questions for the legal and tax team¶
Each of these changes the answer and is outside engineering's remit:
- Disclosed vs undisclosed agency. Under the actual contracts, app UX, and payment flows, does C buy from SP (disclosed, Case 1 or 3) or from P (undisclosed principal, Case 2)? This is the primary fork.
- Deemed-supplier scope (Art. 47(3)). Does the platform meet the electronic-marketplace definition, and does it fall within the per-scenario exception? The exception requires avoiding all five control factors:
- set the terms
- determine the consideration
- collect payment
- handle complaints
- offer promotions or compensation
plus, per §4.6: Scenario A also requires naming the supplier as principal; Scenario B also requires disclosure and a direct supplier-to-customer contract. A payment-collecting platform generally does not meet the exception, so it is the deemed supplier for non-registered or non-resident SPs. For non-resident SPs, also confirm whether the facilitated supply is an electronic (digital) service. Scenario A is limited to electronic services and does not capture physically-performed/on-premises services. 3. SP registration mix. Will the platform admit non-resident or non-VAT-registered SPs? If so, Case 2 is mandatory for them, with reverse-charge or out-of-scope handling of the SP→P supply. 4. Platform-fee expression in Case 2. Retained margin, or an explicit separate fee invoice? Avoid double counting. 5. Authority approvals. Third-party issuance (Art. 53(3)) and self-billing (Art. 53(2)) are subject to the Authority's approval and require documented agreements. Confirm the approvals and agreements before issuing in Cases 2 and 3. 6. Cross-tenant signing and CSID model in Invora for Cases 2 and 3 (which CSID signs, which counter chain advances). Confirm with the Invora team before go-live.
12. References¶
Primary-source files and per-claim locators are in regulations/SOURCES.md. Key mappings:
| Statement | Source file | Locator |
|---|---|---|
| Clearance vs reporting (B2B/B2C) | einvoicing-detailed-guideline-may2023.pdf | §2.15, §2.16 |
| Disclosed vs undisclosed agent | vat-agents-guideline.pdf | §4.2, §6.1 to 6.2 |
| Third-party invoicing; supplier stays responsible | vat-implementing-regulations-full-EN.pdf | Art. 53(3) |
| Self-billing conditions | vat-implementing-regulations-full-EN.pdf | Art. 53(2) |
| ZATCA invoice subtype + transaction flags (NNPNESB) | ZATCA e-invoicing implementation standard (see SOURCES.md) | BR-KSA-06 (NNPNESB structure / 01-02 subtype + flag positions) |
| Third-party and self-billed sample invoices | ZATCA official sample documents (see SOURCES.md) | codes 0110000 / 0100001 |
| Deemed-supplier rule, effective 1 Jan 2026 | vat-implementing-regulations-amended-apr2025-pwc.pdf | Art. 47(3), pp. 2 & 4 |
Invora API paths and request fields correspond to the published proto contract (on the Buf Schema Registry) and the generated API reference.
Related¶
- Simple invoicing — the write surface every case builds on.
- Documents API — the full UBL surface and regulation metadata.
- Billing — metered platform fees (Option B in Case 6).
- Identity & registration — onboarding SPs as connected businesses.
- Multi-tenancy — the platform / connected-business tenant model.
- Webhooks — reacting to document and billing events.
- ZATCA integration — onboarding and submission detail.
- Collecting payments with Tap — connecting Tap, payment links, saved cards, and webhooks.
- List & filtering and Field masks — query and projection conventions.
- Evidence & sources — primary-source files and per-claim locators.
Technical and regulatory reference. Not legal or tax advice. Confirm the questions in §11 with a qualified KSA VAT adviser before implementation.