واجهة API لإنشاء الفواتير

أنشئ فواتير PDF احترافية برمجياً من خادمك، مناسبة للأنظمة الداخلية أو الأسواق أو منتجات SaaS.

1. المصادقة#

أنشئ API key من app.invosend.com/settings/api-keys. يلزم وجود خطة مدفوعة ودور owner.

أرسل المفتاح كـ Bearer token في header Authorization مع كل طلب:

Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx
أي مفتاح ناقص أو بتنسيق خاطئ أو غير صالح أو ملغى سيتم رفضه بـ 403. يتم رفض المفاتيح أيضاً إذا كانت مساحة العمل مجانية أو موقوفة أو منتهية.

2. Endpoint#

POST/api/invoices/generate-external

Base URL: https://app.invosend.com

وصف API قابل للقراءة آليًا: OpenAPI 3.1 وPostman collection. استورد أيًا منهما في Postman أو Insomnia أو مولّد SDK.

3. Request Body#

الحقلالنوعمطلوبالوصف
customer_namestringنعماسم العميل أو مستلم الفاتورة.
customer_emailstringلاعنوان بريد إلكتروني صالح. لا تُرسل الفاتورة التي أُنشئت بهذا الاستدعاء عبر البريد (هذه الواجهة لا ترسل رسائل بريد). إلزامي للفواتير المتكررة: تُرسل كل فاتورة تُنشأ لاحقًا إلى هذا العنوان.
customer_tax_idstringلاالرقم الضريبي للعميل (NPWP في إندونيسيا، حتى 40 حرفًا). يُطبع أسفل عنوان العميل في الفاتورة.
itemsarrayنعمعنصر واحد صالح على الأقل مع وصف وquantity > 0 وprice >= 0.
items[].descriptionstringلااسم المنتج أو الخدمة. إذا تُرك فارغًا يُستخدم Item.
items[].quantitynumberنعمعدد الوحدات، بحد أقصى منزلتين عشريتين (مثل 1.5 ساعة)، من 0.01 إلى 999,999.
items[].pricenumberنعمسعر الوحدة.
items[].discountnumberلاقيمة الخصم لكل عنصر. الافتراضي 0.
currencystringلارمز العملة مثل SAR أو USD أو IDR.
tax_ratenumberلانسبة الضريبة من المجموع بعد الخصم. الافتراضي 0.
due_datestringلاصيغة YYYY-MM-DD. الافتراضي بعد 14 يوماً من الإنشاء.
notesstringلاملاحظات إضافية تظهر على الفاتورة.
items[].unitstringلاوحدة القياس بجانب الكمية، مثل pcs أو ساعة.
items[].tax_ratenumberلانسبة ضريبة مخصصة لهذا العنصر (0-100). اتركه فارغاً لاستخدام tax_rate الخاص بالفاتورة.
discount_totalnumberلاخصم على مستوى الفاتورة. يُخصم قبل الضريبة، لذا تُحسب الضريبة على المبلغ بعد الخصم (ويُوزَّع على البنود بنسبة قيمتها). الافتراضي 0.
templatestringلامعرّف قالب الفاتورة المراد استخدامه. اعرض القوالب المتاحة عبر GET /api/invoices/external/templates. إذا حُذف، يُستخدم القالب الافتراضي المحدد في الإعدادات ثم القالب الافتراضي لـ Invosend.
document_languagestringلالغة الكلمات في مستند الفاتورة (ملف PDF والبريد الإلكتروني): en (الافتراضية)، id, es, fr, de, nl, it, pt, zh, ja, hi, ar (الإندونيسية والإسبانية والفرنسية والألمانية والهولندية والإيطالية والبرتغالية البرازيلية والصينية المبسطة واليابانية والهندية والعربية). تُرفض أي قيمة أخرى مع رسالة خطأ. التخطيط الداخلي البسيط، الذي يُستخدم فقط عند عدم وجود قالب، يكون دائماً بالإنجليزية.
custom_payment_notesstringلاتعليمات دفع إضافية تظهر في ملف PDF.
footer_textstringلانص قصير يظهر أسفل ملف PDF.
is_recurringbooleanلاينشئ سلسلة متكررة. هذه الفاتورة هي الأولى؛ وتُنشأ الفواتير التالية وتُرسل تلقائيًا إلى customer_email وفق الجدول، وتستهلك كل واحدة منها رصيد فاتورة واحدًا. يتطلب باقة تتضمن ميزة الفواتير المتكررة، بالإضافة إلى recurring_interval وcustomer_email.
recurring_intervalstringلامدى تكرار السلسلة: إحدى القيم weekly أو fortnightly أو monthly أو quarterly أو semi-annually أو yearly. إلزامي عندما تكون is_recurring هي true.
recurring_countintegerلاإجمالي عدد الفواتير في السلسلة، بما فيها هذه الأولى: عدد صحيح من 2 إلى 121. القيمة الافتراضية 4. استخدم هذا الحقل أو recurring_end_date، وليس كليهما.
recurring_end_datestringلاالتاريخ الذي لا تُنشأ بعده فواتير جديدة، بصيغة YYYY-MM-DD، في المستقبل. استخدم هذا الحقل أو recurring_count، وليس كليهما.
payment_gatewaystringلاإحدى القيم midtrans أو xendit أو stripe أو paypal أو razorpay أو mollie. يجب أن تكون بوابة الدفع متصلة مسبقاً في مساحة العمل.
payment_accountstringلامعرّف حساب دفع موجود (بيانات بنكية) لإرفاقه بالفاتورة.

مثال طلب كامل:

curl -X POST https://app.invosend.com/api/invoices/generate-external \
  -H "Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_name": "Acme Corp",
    "customer_email": "billing@acme.com",
    "items": [
      { "description": "Consulting Services", "quantity": 2, "price": 2000, "tax_rate": 11 }
    ],
    "discount_total": 200,
    "currency": "SAR",
    "notes": "Thank you for your business."
  }'

مثال على فاتورة متكررة (الأولى الآن ثم واحدة كل شهر، بإجمالي 6):

curl -X POST https://app.invosend.com/api/invoices/generate-external \
  -H "Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_name": "Acme Corp",
    "customer_email": "billing@acme.com",
    "items": [
      { "description": "Monthly retainer", "quantity": 1, "price": 10000 }
    ],
    "currency": "SAR",
    "recurring_interval": "monthly",
    "recurring_count": 6
  }'
لإعادة المحاولة بأمان بعد انتهاء المهلة أو خطأ في الشبكة، أضف ترويسة Idempotency-Key (من 1 إلى 255 حرفًا، مثل UUID تنشئه لكل فاتورة). إذا نجح الطلب الأول، تعيد المحاولة الاستجابة نفسها مع الترويسة Idempotent-Replayed: true دون إنشاء فاتورة أخرى أو استهلاك رصيد آخر (قيمة credits_remaining هي قيمة الطلب الأول). تُحفظ المفاتيح 24 ساعة لكل مساحة عمل. تُحفظ الاستجابات الناجحة فقط، لذا يمكن إعادة محاولة طلب فاشل بالمفتاح نفسه.

4. الاستجابة#

200 OK يعني أن الفاتورة أُنشئت وتم إنشاء ملف PDF:

{
  "data": {
    "id": "cjld2cjxh0000qzrmn831i7rn",
    "invoice_number": "INV-1-202609-0016",
    "pdf_url": "https://cdn.invosend.com/company/1/pdf/Invoice_INV_1_202609_0016_UNPAID_0da2beeabb.pdf",
    "credits_remaining": 24,
    "is_recurring": false,
    "template": {
      "id": "k3p9x2m7c0000abcd1234efgh",
      "name": "Classic",
      "source": "system_default"
    }
  }
}
تكون قيمة credits_remaining هي null عندما تكون الحصة غير محدودة. يُستخدم id لاستدعاء الواجهات الإضافية أدناه. الفواتير المنشأة عبر API تظهر أيضاً في قائمة فواتير مساحة العمل وتستخدم نفس تسلسل الأرقام. تأخذ أرقام الفواتير الشكل INV-{معرّف مساحة العمل}-{YYYYMM}-{التسلسل}. في الفاتورة المتكررة تتضمن الاستجابة أيضًا كائن recurring: schedule_id وinterval وoccurrences وnext_issue_date وend_date. recurring.schedule_id هو المعرّف المستخدم مع نقاط النهاية المتكررة أدناه.

5. أكواد الأخطاء#

الحالةالسبب
400حقول مطلوبة مفقودة أو غير صالحة: customer_name، أو items فارغة، أو لا يوجد بند صالح، أو payment_gateway غير معروف، أو payment_gateway غير متصل بمساحة العمل هذه، أو (في الفاتورة المتكررة) غياب recurring_interval أو customer_email أو عدم صلاحية recurring_count / recurring_end_date. تُعاد هذه الحالة أيضًا إذا تعذّر إنشاء ملف PDF: أعد المحاولة (لا يُخصم أي رصيد).
402رصيد invoice_credits في مساحة العمل فارغ.
403Header Authorization مفقود أو خاطئ، أو API key غير صالح/ملغى، أو الخطة لا تدعم API.
403 (متكررة)تم ضبط is_recurring لكن خطة مساحة العمل لا تشمل الفواتير المتكررة.
409لا يزال طلب بنفس Idempotency-Key قيد المعالجة. أعد المحاولة بعد قليل.
422تم استخدام Idempotency-Key مسبقًا مع محتوى طلب مختلف. استخدم مفتاحًا جديدًا لفاتورة جديدة.
429تم تجاوز حد الطلبات. راجع قسم حد الطلبات.

6. حد الطلبات#

النطاقالحد
لكل مساحة عمل (جميع طلبات API مجتمعة)Professional 10/min · Specialist 20/min · Business 30/min · Enterprise 60/min
لكل عنوان IP (جميع طلبات API مجتمعة)30 طلباً/دقيقة
مفاتيح API النشطة لكل مساحة عملProfessional 3 · Specialist 5 · Business 10 · Enterprise 20
إنشاء الفواتير ونقاط القراءة/الإدارة تشترك في الحد نفسه. تتضمن الاستجابات الناجحة الترويستين X-RateLimit-Limit وX-RateLimit-Remaining. تُضبط حصص API لمساحة العمل حسب باقة العضوية في Invosend. هل تحتاج إلى حجم إنتاج أكبر؟ تواصل معنا من حساب مساحة العمل. المفاتيح الملغاة لا تُحتسب ضمن حد عدد المفاتيح.

7. واجهات إضافية#

يمكن استخدام نفس مفتاح API لقراءة وإدارة الفواتير التي أُنشئت عبر generate-external:

الطريقةالمسارالوصف
GET/api/invoices/externalعرض فواتير مساحة العمل هذه. يدعم ?status= و?page=/?pageSize=.
GET/api/invoices/external/templatesعرض قوالب الفواتير التي يمكنك تحديدها في template، مع بيان ما إذا كانت باقتك تسمح بها وأيها الافتراضي.
GET/api/invoices/external/:idجلب فاتورة واحدة عبر id (القيمة التي تُعيدها generate-external).
PUT/api/invoices/external/:id/statusتحديث حالة الفاتورة. النص: { "status": "paid" } (إحدى unpaid أو paid أو overdue أو cancelled).
PUT/api/invoices/external/:id/archiveأرشفة الفاتورة أو إلغاء أرشفتها. النص: { "is_archived": true }.
POST/api/invoices/external/:id/pdfإعادة جلب رابط PDF، مع إعادة إنشائه عند الحاجة. النص: { "force": true } لفرض إعادة الإنشاء.
GET/api/invoices/external/recurringعرض السلاسل المتكررة. يدعم ?status= (active أو paused أو completed) و?page=/?pageSize=.
GET/api/invoices/external/recurring/:idجلب سلسلة واحدة (قيمة recurring.schedule_id التي يعيدها generate-external) مع جميع دوراتها: التاريخ والحالة وid الفاتورة التي أنتجتها كل دورة.
PUT/api/invoices/external/recurring/:id/statusإيقاف سلسلة مؤقتًا أو استئنافها. النص: { "status": "paused" } أو { "status": "active" }. يتطلب الاستئناف خطة تتضمن الفواتير المتكررة.
PUT/api/invoices/external/recurring/:id/extendإضافة دورات بعد الأخيرة. النص: { "count": 3 } أو { "end_date": "2027-12-31" }.
PUT/api/invoices/external/recurring/:id/frequencyتغيير وتيرة تكرار السلسلة؛ وتُستبدل الدورات القادمة التي لم تُنشأ بعد. النص: { "frequency": "monthly", "start_date": "2027-01-01", "count": 6 } (يمكن استخدام end_date بدلًا من count؛ ويجب أن يكون start_date في المستقبل).
POST/api/invoices/external/recurring/:id/skipحذف دورة قادمة واحدة. النص: { "date": "2027-01-01" }. لا يمكن حذف دورة أنتجت فاتورة بالفعل.
لتحصيل دفعة عبر الإنترنت، اضبط payment_gateway (وpayment_account اختياريًا) في generate-external، ثم أرسل إلى عميلك صفحة الدفع العامة للفاتورة: https://app.invosend.com/p/invoice/{id}، مستخدمًا id الذي تعيده الواجهة. يدفع العميل هناك عبر البوابة المتصلة وتُوسَم الفاتورة تلقائيًا بأنها مدفوعة. تتولى تلك الصفحة إتمام الدفع؛ ولا توجد نقطة دفع عامة منفصلة عليك استدعاؤها.

8. Webhooks#

بدلًا من الاستعلام المتكرر، سجّل عنوان HTTPS وسيرسل Invosend إليه طلب POST بمحتوى JSON كلما تغيّرت فاتورة. أضف نقاط النهاية من الإعدادات > Webhooks (مالك مساحة العمل، وخطة تتضمن الوصول إلى API، وحتى 5 نقاط نهاية). لكل نقطة نهاية سرّ توقيع خاص بها يظهر مرة واحدة فقط.

تُرسل الأحداث للفواتير المنشأة من لوحة التحكم ومن خلال هذه الواجهة على حد سواء. لا تُرسل أبدًا قوالب الفواتير المتكررة.

الحدثيُرسل عندما
invoice.createdيتم إنشاء فاتورة. قد لا يكون ملف PDF جاهزًا بعد، لذا قد تكون قيمة pdf_url هي null.
invoice.paidتتغير الحالة إلى paid (من قِبلك أو عبر الواجهة أو بدفع إلكتروني).
invoice.overdueتتغير الحالة إلى overdue (تلقائيًا بعد تاريخ الاستحقاق أو يدويًا).
invoice.cancelledتتغير الحالة إلى cancelled.

لكل حدث الغلاف نفسه. data.object هو الفاتورة بالشكل نفسه الذي يعيده GET /api/invoices/external/:id؛ وتتضمن أحداث الحالة أيضًا data.previous_status:

{
  "id": "evt_9f2c1b7a4e3d5c6b8a1f0e2d",
  "object": "event",
  "type": "invoice.paid",
  "created": "2026-10-01T09:30:12.000Z",
  "data": {
    "object": {
      "id": "cjld2cjxh0000qzrmn831i7rn",
      "invoice_number": "INV-1-202610-0007",
      "customer_name": "Acme Corp",
      "customer_email": "billing@acme.com",
      "currency": "USD",
      "subtotal": 500,
      "discount_total": 0,
      "tax_rate": 0,
      "tax_amount": 0,
      "amount": 500,
      "amount_paid": 500,
      "amount_credited": 0,
      "amount_received": 500,
      "invoice_status": "paid",
      "is_archived": false,
      "is_recurring": false,
      "recurring_interval": "none",
      "issue_date": "2026-10-01T02:15:40.000Z",
      "due_date": "2026-10-15",
      "paid_at": "2026-10-01T09:30:11.000Z",
      "pdf_url": "https://cdn.invosend.com/company/1/pdf/Invoice_INV_1_202610_0007_PAID.pdf",
      "pdf_status": "ready"
    },
    "previous_status": "unpaid"
  }
}

يحمل كل طلب الترويسات التالية. للتحقق منه، احسب HMAC-SHA256 للقيمة {t}.{النص الخام} بسرّ نقطة النهاية وقارنه بـ v1؛ وارفض الطلب إذا كان t أقدم من 5 دقائق:

POST /your/webhook/url
Content-Type: application/json
Invosend-Event: invoice.paid
Invosend-Delivery: evt_9f2c1b7a4e3d5c6b8a1f0e2d
Invosend-Signature: t=1790847012,v1=5b1d...e9a2

// Node.js
import crypto from "crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(parts.t + "." + rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
أجب بأي رمز حالة 2xx خلال 10 ثوانٍ. وإلا ستُعاد المحاولة حتى 7 مرات إضافية (بعد دقيقة و5 دقائق و30 دقيقة وساعتين و6 ساعات و12 ساعة و24 ساعة). تُوقف نقطة النهاية التي تفشل باستمرار ويصلك إشعار. قد يصل الحدث أكثر من مرة أو بترتيب مختلف: استخدم id الحدث لتجاهل التكرار واعتبر invoice_status المصدر الموثوق. تُحفظ سجلات التسليم 30 يومًا، ويمكن إعادة إرسال أي حدث من الإعدادات > Webhooks.

9. القيود#

هذه الواجهة مخصصة عمداً لإنشاء الفواتير وإدارتها بشكل بسيط. الميزات التالية غير متاحة بعد:

  • تغطي Webhooks فقط إنشاء الفاتورة ودفعها وتأخرها وإلغاءها. أما التغييرات الأخرى مثل التعديل أو الأرشفة فلا تُرسل: اقرأ الحالة الحالية عبر GET /invoices/external/:id.
  • لا تُرسل الفاتورة التي ينشئها كل طلب عبر البريد: هذه الواجهة تُنشئ ملف PDF فقط، وتسليمه إلى عميلك مسؤوليتك (أو استخدم لوحة التحكم). الاستثناء هو السلاسل المتكررة: تُرسل كل فاتورة لاحقة ينشئها المجدوِل تلقائيًا إلى customer_email.
  • لا يمكن تعديل الفاتورة بعد إنشائها — يمكن عبر هذه الواجهة تغيير الحالة وحالة الأرشفة فقط.
  • يتطلب دعم بوابة الدفع أن تكون متصلة مسبقاً من لوحة التحكم؛ هذه الواجهة تُرفقها بالفاتورة فقط ولا تقوم بربط البوابة بنفسها.
  • يمكن إيقاف السلسلة المتكررة مؤقتًا واستئنافها وتمديدها وتغيير جدولها وتخطي دوراتها القادمة عبر API، لكن لا يمكن تعديل محتواها (البنود والعميل والمبالغ) ولا حذفها عبر API؛ نفّذ ذلك من لوحة التحكم.

الأسئلة الشائعة#

كيف أحصل على API key من Invosend؟

أنشئ حساباً في app.invosend.com، ارفع مساحة العمل إلى خطة مدفوعة، ثم افتح Settings > API Keys واضغط Generate key. يظهر المفتاح الأصلي مرة واحدة فقط عند إنشائه.

كم تكلف كل عملية إنشاء فاتورة عبر API؟

كل طلب ناجح يخصم 1 رصيد فاتورة من مساحة العمل، من نفس رصيد الفواتير المستخدمة عبر لوحة التحكم.

ما هو rate limit لهذه الواجهة؟

يتبع حد المعدل لمساحة العمل إعدادات باقتك المدفوعة. يسرد قسم Rate Limit أعلاه الحد الحالي لكل باقة.