المطورون

البدء السريع: أول فاتورة عبر الواجهة البرمجية

من مفتاح API إلى مسودة فاتورة في خمس دقائق، مع أمثلة جاهزة للنسخ بـ cURL و PHP و Node.js و Python.

قبل أن تبدأ

  • منشأة في وندرفاتورة على باقة الأعمال أو المؤسسات؛ فالوصول إلى الواجهة البرمجية جزء منهما. باقة الأعمال تعمل ضمن حدودها الشهرية للفواتير والمستخدمين، وباقة المؤسسات بلا حدود.
  • مستخدم مخوّل بإدارة الإعدادات (مدير المنشأة مخوّل).
  • ملف منشأة مكتمل (الاسم والرقم الضريبي والعنوان) حتى يمكن إصدار الفواتير.
ليس لديه حساب بعد؟ أرسل لمن سيملك الحساب رابطًا واحدًا: https://fatoora.tw.sa/start?intent=api. ينشئ حسابه (أو يسجّل دخوله بالبريد أو Google) ويصل إلى صفحة تنشئ مفتاح API وتعرض العنوان الأساسي وأول طلب؛ وإن كانت باقته لا تشمل الوصول عبر API تبيّن الباقة التي تشمله. أضف &plan=enterprise ليمر أولًا بدفع تلك الباقة، و&lang=en للإنجليزية.

1. أنشئ مفتاح API

  1. سجّل الدخول وافتح الإعدادات ← مفاتيح API.
  2. سمِّ المفتاح باسم النظام الذي سيستخدمه، مثل "المتجر الإلكتروني".
  3. اختر صلاحياته. لهذا الدليل: invoices:write وinvoices:read. أضف invoices:issue فقط عندما تكون مستعدًا لإصدار فواتير ضريبية حقيقية.
  4. اختر مدة صلاحيته (30 أو 90 أو 180 أو 365 يومًا) وأنشئه.
  5. انسخ المفتاح الآن؛ فهو يُعرض مرة واحدة، ولا تحتفظ وندرفاتورة إلا ببصمته.
احفظ المفتاح على خادمك، في متغير بيئة أو مدير أسرار. لا تضعه أبدًا في متصفح أو تطبيق جوال أو مستودع شيفرة.

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 وأرسل أول طلب خلال دقائق.