ZATCA API Client¶
Using the fatoora-core API client for compliance, reporting, and clearance APIs.
Example¶
use fatoora_core::api::{CsidCredentials, Production, ZatcaClient};
use fatoora_core::config::{Config, EnvironmentType};
use fatoora_core::invoice::xml::parse::parse_signed_invoice_xml;
#[tokio::main]
pub async fn main() {
// read and parse the signed XML invoice
let signed_xml_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/invoices/sample-simplified-invoice.xml");
let xml = std::fs::read_to_string(&signed_xml_path).unwrap();
let signed = parse_signed_invoice_xml(&xml).unwrap();
// initialize the API client and credentials
let config = Config::new(EnvironmentType::NonProduction);
let client = ZatcaClient::new(config).unwrap();
// you can get the sandbox credentials from the ZATCA developer portal:
// at https://sandbox.zatca.gov.sa/IntegrationSandbox/reporting-api
let pcsid = CsidCredentials::<Production>::new(
EnvironmentType::NonProduction,
None,
EXAMPLE_BST,
EXAMPLE_SECRET,
);
let response = client
.report_simplified_invoice(&signed, &pcsid, false, Some("en"))
.await
.unwrap();
response.ensure_accepted().expect("reporting accepted");
assert_eq!(response.reporting_status(), Some("REPORTED"));
}
from fatoora.api import CsidProduction, ZatcaClient
from fatoora.config import Config, Environment
from fatoora.invoice import parse_signed_invoice_xml
# signed_xml_path = "path/to/signed_invoice.xml"
signed = parse_signed_invoice_xml(signed_xml_path.read_text(encoding="utf-8"))
config = Config(Environment.NON_PRODUCTION)
client = ZatcaClient(config)
pcsid = CsidProduction.new(
Environment.NON_PRODUCTION,
EXAMPLE_BST,
EXAMPLE_SECRET,
None,
)
response = client.report_simplified_invoice(signed, pcsid, False, "en")
response.ensure_accepted()
assert response.reporting_status() == "REPORTED"
import { Config as ApiConfig, CsidProduction, SignedInvoice, ZatcaClient } from 'fatoority';
export async function reportInvoice(signedXml, token, secret, { signal, fetch: transport = globalThis.fetch } = {}) {
const client = new ZatcaClient(ApiConfig.new_(2), { fetch: transport });
const credentials = CsidProduction.create(2, null, token, secret);
const invoice = SignedInvoice.fromXml(signedXml);
const response = await client.reportSimplifiedInvoice(invoice, credentials, false, 'en', { signal });
response.ensureAccepted();
return response;
}
public static void ReportInvoice(string signedXml, string token, string secret)
{
using var config = Config.New(2); // Production.
using var client = ZatcaClient.Create(config);
using var credentials = CsidProduction.Create(2, null, token, secret);
using var invoice = SignedInvoice.FromXml(signedXml);
using var response = client.ReportSimplifiedInvoice(
invoice, credentials, false, "en");
response.EnsureAccepted(); // HTTP success alone does not mean acceptance.
Console.WriteLine(response.Outcome());
}
// This method performs a blocking network request.
#include "fatoora.h"
#include <stdio.h>
int main(int argc, char **argv) {
if (argc != 4) {
fprintf(argc == 1 ? stdout : stderr,
"Usage: %s signed-invoice.xml token secret\n"
"Submits a simplified invoice to the non-production gateway.\n", argv[0]);
return argc == 1 ? 0 : 1;
}
FatooraConfig *config = NULL;
FatooraSignedInvoice *invoice = NULL;
FatooraCsidProduction *credentials = NULL;
FatooraZatcaClient *client = NULL;
FatooraValidationResponse *response = NULL;
FatooraError *error = NULL;
FatooraStatus status = FATOORA_OK;
int exit_code = 1;
#define TRY(call) do { status = (call); if (status != FATOORA_OK) goto cleanup; } while (0)
TRY(fatoora_signed_invoice_from_file(argv[1], &invoice, &error));
TRY(fatoora_config_new(FATOORA_NON_PRODUCTION, &config, &error));
TRY(fatoora_zatca_client_create(config, &client, &error));
TRY(fatoora_csid_production_create(FATOORA_NON_PRODUCTION, NULL,
argv[2], argv[3], &credentials, &error));
TRY(fatoora_zatca_client_report_simplified_invoice(client, invoice,
credentials, false, "en", &response, &error));
/* Receiving a response does not establish acceptance. */
TRY(fatoora_validation_response_ensure_accepted(response, &error));
puts("Invoice reporting accepted.");
exit_code = 0;
cleanup:
if (exit_code) fprintf(stderr, "Error %d: %s\n%s\n", (int)status,
fatoora_error_message(error), fatoora_error_details_json(error));
fatoora_validation_response_free(response);
fatoora_zatca_client_free(client);
fatoora_csid_production_free(credentials);
fatoora_signed_invoice_free(invoice);
fatoora_config_free(config);
fatoora_error_free(error);
return exit_code;
}
Response receipt and acceptance¶
The three invoice methods return a response when HTTP status is 2xx and the body
can be decoded. Call ensure_accepted() to require acceptance of the invoked
operation. For compliance, acceptance means that the compliance check passed.
Use outcome() to handle Accepted, Rejected, and Unknown yourself. Warnings
alone allow acceptance. Missing, unfamiliar, or contradictory evidence produces
Unknown; inspect the original status fields and validation messages.
For clearance, cleared_invoice_base64() preserves the returned field.
cleared_invoice_xml() decodes nonempty UTF-8 text, preserving whitespace; it
returns None when absent and an error for empty or invalid content, including NUL. Decoding
does not parse XML or verify a signature. Save the returned XML directly when
you need the gateway document. An accepted outcome does not guarantee that this
optional document is present or decodable.
Non-2xx invoice responses return structured errors with http_status, response
text, and parsed JSON when available. Python exposes these in error.details;
C exposes them through fatoora_error_details_json().
Notes¶
Configand credentials must target the same environment (non-production, simulation, or production).- Reporting and clearance APIs require production credentials; compliance checks use compliance credentials.
- Invoice responses use opaque handles with getters in C and Python. Credential endpoints return credential handles and retain their existing response rules.
See also: API Client Reference
C examples include fatoora.h; compile the adapter once with your application.
See the C build instructions.
See the JavaScript / TypeScript reference for installation and runtime contracts.
See the C# / .NET reference for installation, imports, and ownership rules.