This page describes a capability that is not available yet. It is published so you can plan ahead, and details may change before release. Everything marked "Available" in these docs works today.
Today: any HTTP client
You do not need an SDK: JSON in and out over HTTPS, and one header. Every page has copyable examples in cURL, PHP, Node.js and Python.
curl -X POST https://fatoora.tw.sa/api/v1/invoices \
-H "Authorization: Bearer wbk_test_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"issue": false,
"issue_date": "2026-11-03",
"customer": {
"name": "Example Trading Co.",
"vat_number": "300000000000003",
"address_line1": "King Fahd Rd",
"city": "Riyadh",
"postal_code": "12345",
"country": "SA"
},
"items": [
{ "description": "Consulting", "quantity": 2, "unit_price": 500, "vat_rate": 15 }
]
}'
<?php
$response = $client->post('invoices', [
'json' => [
'issue' => false, // a draft: no legal number, nothing sent to ZATCA
'issue_date' => '2026-11-03',
'customer' => [
'name' => 'Example Trading Co.',
'vat_number' => '300000000000003',
'address_line1' => 'King Fahd Rd',
'city' => 'Riyadh',
'postal_code' => '12345',
'country' => 'SA',
],
'items' => [
['description' => 'Consulting', 'quantity' => 2, 'unit_price' => 500, 'vat_rate' => 15],
],
],
'http_errors' => false,
]);
$body = json_decode((string) $response->getBody(), true);
if ($body['ok']) {
echo $body['data']['invoice']['number']; // DRAFT-… until it is issued
} else {
echo $body['error']['code'];
}
const res = await fetch("https://fatoora.tw.sa/api/v1/invoices", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.WONDERFATOORA_API_KEY}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
issue: false, // a draft: no legal number, nothing sent to ZATCA
issue_date: "2026-11-03",
customer: {
name: "Example Trading Co.",
vat_number: "300000000000003",
address_line1: "King Fahd Rd",
city: "Riyadh",
postal_code: "12345",
country: "SA",
},
items: [{ description: "Consulting", quantity: 2, unit_price: 500, vat_rate: 15 }],
}),
});
const body = await res.json();
if (!body.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
console.log(body.data.invoice.id, body.data.invoice.number);
res = requests.post(
"https://fatoora.tw.sa/api/v1/invoices",
headers={
"Authorization": f"Bearer {os.environ['WONDERFATOORA_API_KEY']}",
"Accept": "application/json",
},
json={
"issue": False, # a draft: no legal number, nothing sent to ZATCA
"issue_date": "2026-11-03",
"customer": {
"name": "Example Trading Co.",
"vat_number": "300000000000003",
"address_line1": "King Fahd Rd",
"city": "Riyadh",
"postal_code": "12345",
"country": "SA",
},
"items": [
{"description": "Consulting", "quantity": 2, "unit_price": 500, "vat_rate": 15}
],
},
timeout=30,
)
body = res.json()
print(res.status_code, body["data"]["invoice"]["number"] if body["ok"] else body["error"])
Official SDKs
| Language | Planned package | Requirements |
|---|---|---|
| PHP | wonderfatoora/wonderfatoora-php |
PHP 8.1+ with cURL, no other dependencies. Optional Laravel service provider, facade and webhook middleware. |
| Node.js | @wonderfatoora/sdk |
Node.js 18+, no dependencies, TypeScript types included. |
| Python | wonderfatoora |
Python 3.9+, standard library only, typed. |
What they do
- One method for each API operation, with typed answers, generated from the OpenAPI document and refined by hand.
- An
Idempotency-Keyon every write that accepts one, added automatically, or your own key when you pass it. - Retries after
429and503that wait forRetry-After, only for requests that are safe to repeat. - Typed errors carrying
code,message,detailsand the request id. - Cursor pagination as an iterator, and PDF and XML downloads as bytes.
- Webhook signature checks: constant-time, within a time tolerance, and accepting both secrets during a rotation.
- Versioned with the API's OpenAPI version.
Laravel
See Laravel e-invoicing integration for a service class, configuration and a queued job you can use today.
The OpenAPI 3.1 document is published at /developers/openapi.json: you can generate a client for your language with a standard OpenAPI generator today.
Ready to build?
API access is part of the Business and Enterprise plans. Create a key in Settings → API keys and make your first call in minutes.