واجهة فواترا البرمجية
نقطتا نهاية تكفيان لربط نظامك بنظام الفوترة الوطني: واحدة لإنشاء الفاتورة وإرسالها، وأخرى للاستعلام عن حالتها. نحن نتولّى UBL 2.1 والتصغير والترميز وترويسات الدائرة.
العنوان الأساسي
جميع النداءات تمرّ عبر HTTPS. الطلبات غير المشفّرة تُرفض.
https://fawtara.codexal.co/api/v1
المصادقة
كل طلب يحمل مفتاحك في ترويسة Authorization: Bearer <key> (وتُقبل X-Api-Key كبديل متوافق). نحفظ هاش SHA-256 للمفتاح فقط — لا يمكننا استرجاعه لك، فاحفظه عند إنشائه.
X-Api-Key: fwt_live_7Kd2pQw9xZmR4vT8nL1sB6yH3cF0aJeU
Content-Type: application/json
مفاتيح JoFotara — من أين تأتي
مفتاح فواترا (X-Api-Key) يصادقك لدينا. أما الإرسال الفعلي إلى الدائرة فيتم بمفاتيح منشأتك الصادرة من منصة نظام الفوترة الوطني نفسها. لا يصدرهما أي مزوّد خدمة — تولّدهما بنفسك من شاشة «ربط الأجهزة».
يمكن للمستخدم الرئيسي (Admin) من خلال شاشة ربط الأجهزة ربط برنامجه الخاص لإصدار الفواتير (Integration) ليتم ترحيل الفواتير من خلاله.
الخطوات كما وردت في الدليل
- الدخول إلى نظام الفوترة الإلكتروني بالمستخدم الرئيسي (Admin).
- الضغط على «ربط الأجهزة» من الشاشة الرئيسية.
- الضغط على زر «ربط جديد».
- إدخال اسم المستخدم للربط وتحديد تسلسل مصدر الدخل ثم الضغط على زر الإضافة.
- الضغط على زر المتابعة للرسالة التنبيهية.
نصّ الرسالة التنبيهية في الخطوة الأخيرة:
لن يتمكن المكلف من تنظيم الفواتير باستخدام المنصة إذا قام بالربط مع الأجهزة.
بعد تأكيد الربط يصبح نظامك المصدر الوحيد لفواتير ذلك التسلسل، ولا يعود بالإمكان إصدارها يدوياً من المنصة. اختبر كل مساراتك في Sandbox أولاً.
البيئتان
البادئة في المفتاح تحدّد البيئة — لا يوجد عنوان منفصل للاختبار.
| البادئة | البيئة | الإرسال الحكومي | تُحتسب من الحصّة |
|---|---|---|---|
| fwt_test_ | Sandbox | لا — محاكاة فقط | لا |
| fwt_live_ | Live | نعم | نعم |
في Sandbox يجري التحقّق الكامل وبناء UBL فعلياً، لكن الملف لا يُرسل إلى الدائرة وتعود الحالة sandbox_accepted. هذا يتيح اختبار منطقك كاملاً دون استهلاك أرقام فواتير حقيقية.
إنشاء فاتورة وإرسالها
النقطة الأساسية: تبني UBL 2.1، تتحقّق منه، ترسله إلى نظام الفوترة الوطني، وتعيد النتيجة في الاستجابة نفسها.
curl -X POST https://fawtara.codexal.co/api/v1/invoices \
-H "X-Api-Key: fwt_live_•••••••••" \
-H "Idempotency-Key: INV-2026-0418" \
-H "Content-Type: application/json" \
-d '{
"invoice_number": "INV-2026-0418",
"invoice_type": "388",
"payment_method": "011",
"invoice_date": "2026-04-18",
"currency": "JOD",
"buyer": {
"name": "شركة النور للتجارة",
"tin": "9871234",
"city": "Amman",
"phone": "0791234567"
},
"items": [
{ "name": "استشارات تقنية", "qty": 1, "unit_price": 450, "tax_rate": 16 },
{ "name": "ترخيص سنوي", "qty": 2, "unit_price": 120, "discount": 20, "tax_rate": 16 }
]
}'
استجابة ناجحة — 201 Created
201 تعني قبول الدائرة للفاتورة. إن عادت 202 فقد أُنشئت لدينا ولم تُقبل بعد — اقرأ data.error ولا تعتبرها نجاحاً.
{
"data": {
"id": "inv_5c8d1e9a2b7f3046",
"object": "invoice",
"mode": "live",
"status": "accepted",
"invoice_number": "INV-2026-0418",
"uuid": "8f3c1e42-5a7b-4c9d-9e10-2b7a64f3b41d",
"icv": 42,
"type_code": "388",
"type_name": "012",
"totals": {
"subtotal": 690.000,
"discount_total": 20.000,
"tax_total": 107.200,
"total": 777.200
},
"qr_content": "AQ3YtNix…",
"qr_image_url": "https://fawtara.codexal.co/api/v1/invoices/inv_5c8d1e9a2b7f3046/qr.png",
"ubl_url": "https://fawtara.codexal.co/api/v1/invoices/inv_5c8d1e9a2b7f3046/ubl",
"jofotara": { "status": "SUBMITTED", "http_code": 200 },
"error": null,
"submitted_at": "2026-04-18 09:14:03"
}
}
فشل التحقّق — 422
لا نستهلك نداءً حكومياً إن كانت الفاتورة سترفَض. حقل fields يسمّي كل حقل خاطئ بمساره الكامل.
{
"error": {
"type": "validation_error",
"code": "validation_error",
"message": "بيانات الطلب غير صحيحة.",
"fields": {
"payment_method": "طريقة الدفع غير متوافقة مع نوع الفاتورة. المسموح: 013 أو 023.",
"seller.income_seq": "تسلسل مصدر الدخل مطلوب — حقل إلزامي لدى الدائرة.",
"items[1].qty": "الكمية يجب أن تكون أكبر من صفر."
}
}
}
حقول الطلب
| الحقل | النوع | إلزامي | الوصف |
|---|---|---|---|
| invoice_number | string | نعم | رقم الفاتورة لديك. يجب أن يكون فريداً. |
| invoice_type | string | نعم | 388 فاتورة ضريبية، 381 إشعار دائن. |
| payment_method | string | نعم | مع 388: 011 نقدي أو 021 ذمم. مع 381: 013 أو 023. |
| invoice_date | string | لا | بصيغة YYYY-MM-DD. الافتراضي تاريخ اليوم. |
| currency | string | لا | JOD (افتراضي) أو USD أو EUR. |
| seller | object | لا | يتجاوز بيانات البائع المحفوظة مع المفتاح. الحقول: name, tin, income_seq, street, city, country. |
| buyer | object | نعم | name مطلوب. اختيارياً: tin, city, country, phone. |
| items[] | array | نعم | بند واحد على الأقل. لكل بند: name, qty, unit_price, واختيارياً discount و tax_rate. |
| dry_run | boolean | لا | يبني ويتحقّق دون إرسال حكومي. مفاتيح Sandbox تعمل بهذا الوضع دائماً. |
| include_xml | boolean | لا | يُرجع ملف UBL المُولَّد ضمن الاستجابة للتدقيق. |
مطابقة حقول شاشة المنصة بحقول الـAPI
إن كنت تصدر فواتيرك اليوم يدوياً من منصة الـPortal، فهذه الخريطة تختصر عليك مرحلة التحليل: كل حقل في شاشة «تنظيم فاتورة» وما يقابله في نداء POST /api/v1/invoices.
يتم تمييز الحقول الإجبارية بوجود علامة *.
| حقل الشاشة | ما يقابله في الـAPI | ملاحظة من الدليل |
|---|---|---|
| رقم فاتورة البائع | invoice_number | الرقم التسلسلي للفاتورة الخاص بالبائع. |
| تاريخ إصدار الفاتورة * | invoice_date | يُضبط بتاريخ اليوم مع إمكانية تغييره لتاريخ سابق — لا لتاريخ لاحق. |
| نوع العملة * | currency | في المنصة لا يمكن تغييره بعد إضافة أول سلعة. عبر الـAPI تُرسل الفاتورة بعملة واحدة دفعة واحدة. |
| فاتورة نقدية / فاتورة ذمم | payment_method | 011 نقدي، 021 ذمم (ومع الإشعار الدائن 013 و023). |
| نوع الفاتورة: محلية / تصدير / مناطق تنموية * | items[].tax_rate + buyer.tin | «محلية» مختارة تلقائياً. «تصدير» و«مناطق تنموية/تشجيع استثمار» بنسبة ضريبة 0%، والثانية يجب فيها إدخال الرقم الضريبي للمشتري. |
| بيانات البائع | seller (اختياري) | في المنصة تُدخل تلقائياً من بيانات التسجيل. لدينا تُؤخذ من إعدادات المفتاح، وseller يتجاوزها عند الحاجة. |
| اسم المشتري | buyer.name | إجباري لفواتير الذمم وللفواتير بقيمة 10,000 وأكثر. |
| نوع المعرفات الإضافية + رقم المشتري | buyer.tin | الرقم الوطني أو الرقم الضريبي أو الرقم الشخصي. |
| رقم الهاتف / المدينة / الرمز البريدي | buyer.phone، buyer.city | اختيارية، لكنها تُحسّن قابلية مطابقة الفاتورة لدى المشتري. |
| وصف السلعة أو الخدمة * | items[].name | تفاصيل السلعة أو الخدمة. |
| الكمية * / سعر الوحدة * | items[].qty، items[].unit_price | لا ترسل مجاميع — نحسبها كما تحسبها المنصة. |
| قيمة الخصم | items[].discount | خصم على مستوى البند، يُطرح قبل احتساب الضريبة. |
| نسبة الضريبة العامة * | items[].tax_rate | لا تظهر لفواتير الدخل. يمكن أن تختلف النسبة من بند لآخر. |
| النوع (سلعة/خدمة أو بدل خدمة) * | — | لا تظهر لفواتير الدخل. نضبطها تلقائياً في UBL بما يوافق نوع المكلف المسجّل مع المفتاح. |
| التصنيف الوطني للأنشطة | — | اختياري في الشاشة. نرسله من إعدادات منشأتك عند ضبطه. |
| قيمة الضريبة الخاصة * | تواصل معنا | تخصّ فواتير الضريبة الخاصة فقط. راسلنا لتفعيلها على مفتاحك. |
أشكال الفواتير الثلاثة حسب نوع المكلف
فاتورة الدخل: الفواتير التي لا تحتوي على ضرائب.
فاتورة الضريبة العامة: الفواتير التي تحتوي على ضرائب المبيعات.
فاتورة الضريبة الخاصة: الفواتير التي تحتوي على الضريبة العامة للمبيعات والضريبة الخاصة.
الشكل الذي يحقّ لك إصداره محدَّد بتصنيف منشأتك لدى الدائرة، لا باختيارك في النداء. إن أرسلت tax_rate على مفتاح مسجَّل كمكلف ضريبة دخل، سيعود الرفض من الدائرة بالخطأ government_rejected.
المرتجعات: ما يقوله الدليل وما يعنيه لك برمجياً
لإتمام عملية إرجاع فاتورة، يمكن للمستخدمين إرجاع فاتورة مصدرة عن طريق منصة الـPortal فقط.
هذه الجملة تصف شاشة الإرجاع داخل المنصة: زر «إرجاع» المرتبط بفاتورة قائمة متاح من واجهة الـPortal، وفيه تحدّد الكمية المراد إرجاعها لكل بند.
برمجياً لا تحتاج إلى تلك الشاشة: المرتجع مستند مستقل من نوع 381 (إشعار دائن) ترسله بنفس نقطة النهاية، مع طريقة دفع 013 أو 023 وبنود تحمل الكميات المرتجعة.
الاقتباسات أعلاه من «نظام الفوترة الوطني — دليل المستخدم الخاص بمنصة الـPortal»، واللقطات من الدليل نفسه وتعود ملكيتها لجهة إصداره. أُدرجت للتوضيح التقني فقط، والدليل الرسمي هو المرجع عند أي اختلاف.
الاستعلام عن فاتورة
اجلب الفاتورة بمعرّفها لدينا (inv_…) أو برقمها لديك مباشرة — فلا تحتاج لتخزين معرّفنا إن لم ترغب.
curl "https://fawtara.codexal.co/api/v1/invoices/INV-2026-0418" \
-H "Authorization: Bearer fwt_live_•••••••••"
// 200 OK
{
"data": {
"id": "inv_5c8d1e9a2b7f3046",
"status": "accepted",
"invoice_number": "INV-2026-0418",
"uuid": "8f3c1e42-…-b41d",
"qr_content": "AQ3YtNix…",
"jofotara": { "status": "SUBMITTED", "http_code": 200 }
}
}
وللسرد والتصفية: GET /api/v1/invoices?status=rejected&from=2026-10-01&limit=50 — يقبل أيضاً uuid و invoice_number و mode.
فحص المفتاح
نداء خفيف للتأكد من صحة المفتاح وبيئته قبل بناء التكامل.
curl https://fawtara.codexal.co/api/v1/ping -H "X-Api-Key: fwt_test_•••"
{
"ok": true,
"environment": "sandbox",
"plan": "growth",
"rate_limit": "60/min",
"jofotara_configured": false
}
منع الإرسال المزدوج
أرسل ترويسة Idempotency-Key مع كل طلب إنشاء — استخدم رقم الفاتورة لديك. إن تكرّر النداء بالمفتاح نفسه (انقطاع شبكة، إعادة محاولة تلقائية) نعيد الاستجابة الأصلية حرفياً مع ترويسة Idempotent-Replay: true، ولا تُرسل الفاتورة مرتين إلى الدائرة.
Webhooks
بدل الاستعلام الدوري، سجّل عنوان https من لوحة التحكم أو عبر POST /api/v1/webhooks، ونرسل إليه الحدث فور وقوعه. متاح في خطّتي النمو والاحتراف.
// الترويسات
X-Fawtara-Event: invoice.accepted
X-Fawtara-Delivery: 1842
X-Fawtara-Timestamp: 1791234567
X-Fawtara-Signature: sha256=5e884898da28… // HMAC‑SHA256 لـ "timestamp.body"
// الجسم
{
"id": "evt_6d1e8f3a0b2c4759",
"object": "event",
"type": "invoice.accepted",
"created_at": "2026-04-18T09:14:05+00:00",
"mode": "live",
"data": { … كائن الفاتورة كاملاً … }
}
الأحداث: invoice.accepted · invoice.rejected · invoice.failed · subscription.updated. احسب HMAC_SHA256(timestamp + "." + raw_body, secret) على الجسم الخام قبل أي تحليل، وأعد 2xx خلال 15 ثانية. نعيد المحاولة ستّ مرات بتدرّج: فوراً، دقيقة، 5 دقائق، 30 دقيقة، ساعتان، 6 ساعات. أمثلة التحقّق من التوقيع بثلاث لغات.
حدود المعدّل
الحد يعتمد على خطتك، ويُحتسب في نافذة دقيقة واحدة لكل مفتاح. كل استجابة تحمل الترويسات:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 1791234600
X-Fawtara-Mode: live
| الخطة | طلب / دقيقة | فواتير حقيقية / شهر | مفاتيح |
|---|---|---|---|
| البداية | 30 | 50 | 1 |
| النمو | 120 | 1,000 | 3 |
| الاحتراف | 600 | 10,000 | 10 |
| المؤسسات | حسب الاتفاق | حسب الاتفاق | غير محدود |
بيئة الاختبار غير محدودة في كل الخطط. عند بلوغ الحصّة الحقيقية يعود 402 quota_exceeded مع رابط الترقية — بلا رسوم تجاوز.
أكواد الأخطاء
كل خطأ يعود بالشكل نفسه: { "error": { "type", "code", "message", … } }. اعتمد على type في منطقك (وcode نسخة مطابقة للتوافق)، لا على نص الرسالة.
| HTTP | code | المعنى |
|---|---|---|
| 400 | invalid_body | جسم الطلب فارغ أو ليس JSON صالحاً. |
| 401 | missing_api_key | لم تُرسل ترويسة Authorization أو X-Api-Key. |
| 401 | invalid_api_key | المفتاح غير معروف لدينا. |
| 401 | revoked_api_key | المفتاح أُبطل من لوحة التحكم. |
| 401 | expired_api_key | انتهت صلاحية المفتاح. |
| 402 | quota_exceeded | استُنفدت حصة الخطة الشهرية — يحمل الردّ quota و used و upgrade_url. |
| 402 | subscription_inactive | انتهى أمد الاشتراك أو تأخّر سداده — الإرسال الحقيقي موقوف حتى تأكيد الدفع. |
| 403 | insufficient_scope | المفتاح لا يملك الصلاحية المطلوبة — يحمل الردّ required_scope. |
| 403 | plan_limit | الميزة غير متاحة في خطتك (Webhooks أو الإشعارات الدائنة). |
| 403 | account_suspended | الحساب موقوف إدارياً. |
| 404 | invoice_not_found | لا توجد فاتورة بهذا المعرّف في حسابك. |
| 404 | qr_unavailable | الفاتورة لم تُقبل بعد، فلا رمز QR لها. |
| 405 | method_not_allowed | طريقة HTTP غير مدعومة لهذا المسار. |
| 409 | duplicate_invoice_number | رقم الفاتورة مستخدم مسبقاً — يحمل الردّ existing_invoice_id. |
| 409 | jofotara_credentials_missing | مفتاح إنتاج دون ضبط Client-Id و Secret-Key في الإعدادات. |
| 409 | already_accepted | محاولة إعادة إرسال فاتورة مقبولة أصلاً. |
| 409 | too_many_attempts | تجاوزت 10 محاولات إرسال لنفس الفاتورة. |
| 422 | validation_error | الفاتورة لم تجتز التحقّق المسبق — راجع fields لمعرفة كل حقل خاطئ. |
| 429 | rate_limited | تجاوزت حدّ الطلبات في الدقيقة — انتظر قيمة Retry-After. |
| 500 | internal_error | عطل مؤقت لدينا. أعد المحاولة بتراجع أسّي بنفس Idempotency-Key. |
أمثلة جاهزة
نفس النداء بأربع لغات — انسخه وبدّل المفتاح.
$payload = [
'invoice_number' => 'INV-2026-0418',
'invoice_type' => '388',
'payment_method' => '011',
'buyer' => ['name' => 'شركة النور', 'tin' => '9871234'],
'items' => [
['name' => 'استشارات', 'qty' => 1, 'unit_price' => 450, 'tax_rate' => 16],
],
];
$ch = curl_init('https://fawtara.codexal.co/api/v1/invoices');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Api-Key: ' . getenv('FAWTARA_KEY'),
'Idempotency-Key: ' . $payload['invoice_number'],
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
CURLOPT_TIMEOUT => 45,
]);
$res = json_decode(curl_exec($ch), true);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code === 201) {
echo "معتمدة: {$res['uuid']}";
} else {
echo "خطأ: " . $res['error']['code'];
}
import os, requests
payload = {
"invoice_number": "INV-2026-0418",
"invoice_type": "388",
"payment_method": "011",
"buyer": {"name": "شركة النور", "tin": "9871234"},
"items": [
{"name": "استشارات", "qty": 1, "unit_price": 450, "tax_rate": 16},
],
}
r = requests.post(
"https://fawtara.codexal.co/api/v1/invoices",
json=payload,
headers={
"X-Api-Key": os.environ["FAWTARA_KEY"],
"Idempotency-Key": payload["invoice_number"],
},
timeout=45,
)
if r.status_code == 201:
print("معتمدة:", r.json()["uuid"])
else:
print("خطأ:", r.json()["error"]["code"])
const payload = {
invoice_number: "INV-2026-0418",
invoice_type: "388",
payment_method: "011",
buyer: { name: "شركة النور", tin: "9871234" },
items: [
{ name: "استشارات", qty: 1, unit_price: 450, tax_rate: 16 },
],
};
const res = await fetch("https://fawtara.codexal.co/api/v1/invoices", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": process.env.FAWTARA_KEY,
"Idempotency-Key": payload.invoice_number,
},
body: JSON.stringify(payload),
});
const data = await res.json();
if (res.status === 201) console.log("معتمدة:", data.uuid);
else console.error("خطأ:", data.error.code);
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key",
Environment.GetEnvironmentVariable("FAWTARA_KEY"));
http.DefaultRequestHeaders.Add("Idempotency-Key", "INV-2026-0418");
var payload = new {
invoice_number = "INV-2026-0418",
invoice_type = "388",
payment_method = "011",
buyer = new { name = "شركة النور", tin = "9871234" },
items = new[] {
new { name = "استشارات", qty = 1, unit_price = 450, tax_rate = 16 }
}
};
var res = await http.PostAsJsonAsync(
"https://fawtara.codexal.co/api/v1/invoices", payload);
Console.WriteLine(await res.Content.ReadAsStringAsync());
جاهز للربط؟
احصل على مفتاح Sandbox مجاني وابدأ التجربة اليوم. عند جاهزيتك، نفعّل المفتاح الحي بنقرة.