قبل أن تبدأ
- منشأة في وندرفاتورة على باقة الأعمال أو المؤسسات؛ فالوصول إلى الواجهة البرمجية جزء منهما. باقة الأعمال تعمل ضمن حدودها الشهرية للفواتير والمستخدمين، وباقة المؤسسات بلا حدود.
- مستخدم مخوّل بإدارة الإعدادات (مدير المنشأة مخوّل).
- ملف منشأة مكتمل (الاسم والرقم الضريبي والعنوان) حتى يمكن إصدار الفواتير.
https://fatoora.tw.sa/start?intent=api. ينشئ حسابه (أو يسجّل دخوله بالبريد أو Google) ويصل إلى صفحة تنشئ مفتاح API وتعرض العنوان الأساسي وأول طلب؛ وإن كانت باقته لا تشمل الوصول عبر API تبيّن الباقة التي تشمله. أضف &plan=enterprise ليمر أولًا بدفع تلك الباقة، و&lang=en للإنجليزية.
1. أنشئ مفتاح API
- سجّل الدخول وافتح الإعدادات ← مفاتيح API.
- سمِّ المفتاح باسم النظام الذي سيستخدمه، مثل "المتجر الإلكتروني".
- اختر صلاحياته. لهذا الدليل:
invoices:writeوinvoices:read. أضفinvoices:issueفقط عندما تكون مستعدًا لإصدار فواتير ضريبية حقيقية. - اختر مدة صلاحيته (30 أو 90 أو 180 أو 365 يومًا) وأنشئه.
- انسخ المفتاح الآن؛ فهو يُعرض مرة واحدة، ولا تحتفظ وندرفاتورة إلا ببصمته.
2. تحقق من المفتاح
curl https://fatoora.tw.sa/api/v1/ping \
-H "Authorization: Bearer wbk_test_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Accept: application/json"
<?php
// composer require guzzlehttp/guzzle
$client = new GuzzleHttp\Client([
'base_uri' => 'https://fatoora.tw.sa/api/v1/',
'headers' => [
'Authorization' => 'Bearer ' . getenv('WONDERFATOORA_API_KEY'),
'Accept' => 'application/json',
],
'timeout' => 30,
]);
$response = $client->get('ping');
$body = json_decode((string) $response->getBody(), true);
echo $body['data']['tenant'];
// Node.js 18+ (built-in fetch)
const res = await fetch("https://fatoora.tw.sa/api/v1/ping", {
headers: {
Authorization: `Bearer ${process.env.WONDERFATOORA_API_KEY}`,
Accept: "application/json",
},
});
const body = await res.json();
console.log(body.ok, body.data?.tenant);
# pip install requests
import os
import requests
res = requests.get(
"https://fatoora.tw.sa/api/v1/ping",
headers={
"Authorization": f"Bearer {os.environ['WONDERFATOORA_API_KEY']}",
"Accept": "application/json",
},
timeout=30,
)
print(res.status_code, res.json())
المفتاح الصالح يرد بـ 200 واسم المنشأة:
{
"ok": true,
"data": {
"authenticated": true,
"tenant": "Example Trading Co.",
"time": "2026-11-03T09:12:44+03:00"
}
}
3. أنشئ مسودة فاتورة
المسودة تحمل مرجعًا DRAFT-… بلا رقم نظامي، ولا يُرسل شيء إلى الهيئة. إنها الطريقة الآمنة لتجربة الواجهة مع منشأة حقيقية: راجع المسودة ثم أصدرها عبر POST /invoices/{id}/issue أو من لوحة التحكم، أو ألغها من هناك. ولتجربة كل شيء دون المساس بمنشأتك الحقيقية استخدم بيئة الاختبار.
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"])
الرد 201 Created مع الفاتورة. هكذا تبدو الفاتورة الصادرة:
{
"ok": true,
"data": {
"invoice": {
"id": 1024,
"number": "INV-00045",
"number_is_final": true,
"status": "issued",
"source": "api",
"currency": "SAR",
"issue_date": "2026-11-03",
"due_date": null,
"customer": { "id": 381, "name": "Example Trading Co.", "vat_number": "300000000000003" },
"subtotal": 1000.00,
"discount_total": 0.00,
"vat_total": 150.00,
"total": 1150.00,
"items": [
{
"description": "Consulting",
"quantity": 2,
"unit": "",
"unit_price": 500.00,
"discount": 0.00,
"vat_rate": 15.00,
"vat_amount": 150.00,
"total": 1150.00
}
],
"public_url": "https://fatoora.tw.sa/i/…",
"zatca": {
"flow": "clearance",
"status": "pending",
"with_warnings": false,
"uuid": "…",
"deliverable": false,
"errors": []
},
"created_at": "2026-11-03T09:12:44+03:00"
}
}
}
4. أصدر فاتورة ضريبية
احذف "issue": false (الإصدار هو الافتراضي) وامنح المفتاح صلاحية invoices:issue. الإصدار يمنح الفاتورة الرقم التالي في تسلسلك وينشئ رمز QR، ويرسلها إلى الهيئة إن كانت منشأتك مرتبطة بها. لا يمكن تعديل الفاتورة الصادرة أو حذفها؛ يكون التصحيح بإشعار دائن أو مدين (POST /invoices/{id}/notes، أو من لوحة التحكم).
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 '{
"external_reference": "ORDER-10045",
"issue_date": "2026-11-03",
"customer": { "id": 381 },
"items": [
{ "description": "Annual subscription", "quantity": 1, "unit_price": 1200 }
]
}'
<?php
// Issuing is the default when "issue" is omitted. The key needs
// invoices:write AND invoices:issue. external_reference makes the call
// safe to retry: a repeat returns the invoice already created.
$response = $client->post('invoices', [
'json' => [
'external_reference' => 'ORDER-10045',
'issue_date' => '2026-11-03',
'customer' => ['id' => 381],
'items' => [
['description' => 'Annual subscription', 'quantity' => 1, 'unit_price' => 1200],
],
],
'http_errors' => false,
]);
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({
external_reference: "ORDER-10045", // safe to retry
issue_date: "2026-11-03",
customer: { id: 381 },
items: [{ description: "Annual subscription", quantity: 1, unit_price: 1200 }],
}),
});
const body = await res.json();
// 201 = created now, 200 with data.replayed = true = created by an earlier call
console.log(res.status, body.data?.replayed === true, body.data?.invoice?.zatca);
res = requests.post(
"https://fatoora.tw.sa/api/v1/invoices",
headers={"Authorization": f"Bearer {os.environ['WONDERFATOORA_API_KEY']}",
"Accept": "application/json"},
json={
"external_reference": "ORDER-10045", # safe to retry
"issue_date": "2026-11-03",
"customer": {"id": 381},
"items": [{"description": "Annual subscription", "quantity": 1, "unit_price": 1200}],
},
timeout=60,
)
external_reference (رقم طلبك أو دفعتك) عند الإصدار. إن اضطرتك مهلة منتهية لإعادة المحاولة، تعيد الواجهة الفاتورة التي أنشأها الطلب الأول (200، "replayed": true) بدل إصدار فاتورة ثانية.
5. اقرأ الفاتورة
curl https://fatoora.tw.sa/api/v1/invoices/1024 \
-H "Authorization: Bearer wbk_test_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Accept: application/json"
<?php
$response = $client->get('invoices/1024', ['http_errors' => false]);
$invoice = json_decode((string) $response->getBody(), true)['data']['invoice'] ?? null;
if ($invoice && $invoice['zatca']['deliverable']) {
// Safe to hand to the buyer: send $invoice['public_url'].
}
const res = await fetch("https://fatoora.tw.sa/api/v1/invoices/1024", {
headers: {
Authorization: `Bearer ${process.env.WONDERFATOORA_API_KEY}`,
Accept: "application/json",
},
});
const { ok, data, error } = await res.json();
if (ok && data.invoice.zatca.deliverable) {
// Safe to hand to the buyer: data.invoice.public_url
}
res = requests.get(
"https://fatoora.tw.sa/api/v1/invoices/1024",
headers={"Authorization": f"Bearer {os.environ['WONDERFATOORA_API_KEY']}",
"Accept": "application/json"},
timeout=30,
)
invoice = res.json().get("data", {}).get("invoice")
if invoice and invoice["zatca"]["deliverable"]:
print(invoice["public_url"])
تحقق من zatca.deliverable قبل إرسال الفاتورة لعميلك: الفاتورة الضريبية (بين المنشآت) لمنشأة مرتبطة بالهيئة لا تصبح سارية إلا بعد اعتماد الهيئة لها. راجع حالة الهيئة.
الخطوات التالية
- المصادقة: الصلاحيات والتجديد، وكيف يعمل المفتاح باسم منشئه.
- الفواتير: كل الحقول والعملات والخصومات والإعفاءات.
- الأخطاء وحل المشكلات: كل رمز خطأ وما تفعله حياله.
wbk_test_xxxxxxxxxxxxxxxxxxxxxxxx. مفاتيح التشغيل تبدأ بـ wbk_، ومفاتيح منشأة بيئة الاختبار تبدأ بـ wbk_test_.
صفحات ذات صلة
جاهز للبدء؟
الوصول إلى الواجهة البرمجية ضمن باقات: الأعمال والمؤسسات. أنشئ مفتاحًا من الإعدادات ← مفاتيح API وأرسل أول طلب خلال دقائق.