WidersApps WidersApps

واجهة WidersApps البرمجية

واجهة REST لبيانات WidersApps — جهات الاتصال والمحادثات والطلبات والحجوزات. JSON دخولاً وخروجاً، بغلاف موحّد واحد، ومؤمّنة بمفتاح Bearer تنشئه بنفسك.

العنوان الأساسي

كل نقطة نهاية تقع تحت مسار أساسي واحد يحمل رقم الإصدار. أرسِل كل الطلبات عبر HTTPS.

https://console.widers.net/api/v1

الموارد

الموارد الأساسية والصلاحية التي تحتاجها كل قدرة. هذه مجموعة مختارة للبدء — والمرجع التفاعلي يسرد كل نقطة نهاية تكشفها الواجهة، بتفاصيل الطلب والاستجابة لكلٍّ منها.

الموردما يمكنك فعلهالنطاق
جهات الاتصالعرض القائمة · عرض واحد · إنشاء · تعديل · حذفcontacts.view · contacts.manage
المنتجاتعرض القائمة · عرض واحد · إنشاء وتحديث الحقول الأساسية (الاسم، السعر، الحالة، المخزون — لمزامنة الكتالوج والمخزون)commerce.view · commerce.manage
الطلباتعرض القائمة · عرض واحد · إنشاء · تقديم الحالةcommerce.view · commerce.manage
الرسائلعرض القائمة · عرض واحد · إرسالinbox.access
المحادثاتعرض القائمة · عرض واحد · إسناد · تحويل · إغلاق · إعادة فتح · تعليم مقروء · كتم · أرشفة · متابعة · ملاحظة · وسمinbox.access
الحجوزاتCRUD كامل + الأوقات المتاحةcalendar.view · calendar.manage
الوسومعرض الوسوم؛ وإنشاء/إعادة تسمية/حذف وسومك (وسوم النظام للقراءة فقط)inbox.access · tags.manage
الوسائطرفع ملف والحصول على رابط لإرفاقه عند إرسال رسالةinbox.access
الردود السريعةعرض وإنشاء الردود السريعة المحفوظةquick-replies.manage
الحملاتعرض الحملات وإحصاءاتها؛ وإيقاف/استئناف/إلغاء حملة جاريةcampaigns.view · campaigns.manage
إنشاء الطلب يرفعه دائماً بحالة «قيد الانتظار» — لا تُقبل أي حالة عند الإنشاء، فلا يُحتسب دخل بلا دفع. تقديم الطلب إلى حالة مدفوعة يختم paid_at دون تحصيل أي مبلغ (لمسارات الدفع اليدوي أو التحويل البنكي أو الدفع عند الاستلام) ويتطلّب صلاحية commerce.manage.

حالات تسليم الرسائل وقراءتها وفشلها تصل عبر الويب هوك (message.delivered · message.read · message.failed) لا كنقاط نهاية تُستعلَم. وتغيّرات دورة حياة المحادثة (إنشاء · إسناد · إغلاق · وسم · …) ويب هوك أيضاً — اشترك فيها من الإعدادات ← الويب هوك.

إنشاء مفتاح API

أنشئ مفتاحاً من لوحة التحكّم عبر: الإعدادات ← المطوّرون ← مفاتيح API (للمالك فقط). عند الإنشاء تختار:

  • اسماً — لتتعرّف عليه وتُبطله لاحقاً.
  • النطاقات — لا يمكنك منح إلا النطاقات التي تملكها أنت.
  • مباشر أم اختبار — مفتاح الاختبار (Sandbox) لا يمسّ بيانات حقيقية أبداً.
  • تاريخ انتهاء اختياري.

يُعرض المفتاح كاملاً مرّة واحدة عند الإنشاء. انسخه حينها واحفظه بأمان — وإن فقدته فأبطِله وأنشئ غيره.

المصادقة

صادِق كل طلب بمفتاح Bearer في ترويسة Authorization. الصق المفتاح تماماً كما عُرض لك، ببادئته:

Authorization: Bearer wa_live_<id>|<token>

مفتاح الاختبار يحمل البادئة wa_test_ بدلاً من ذلك:

Authorization: Bearer wa_test_<id>|<token>
البادئة wa_live_ / wa_test_ شكليّة فقط — تُزال قبل تحليل المفتاح. الوضع المباشر مقابل الاختبار يُفرَض بصلاحية المفتاح، لا بالبادئة، فتغيير البادئة لا يغيّر ما يقدر عليه المفتاح.

كل مستخدم ينتمي لشركة واحدة فقط، فالمفتاح يحدّد الحساب الذي يعمل عليه — لا وسيط للمستأجر تمرّره.

النطاقات (Scopes)

النطاقات هي أسماء صلاحيات المنصّة نفسها، فلا يقدر المفتاح على أكثر ممّا يقدر عليه مستخدم اللوحة صاحب الصلاحيات ذاتها. النطاقات المتاحة:

النطاقالوصف
contacts.view قراءة جهات الاتصال
contacts.manage إدارة جهات الاتصال
inbox.access الرسائل (قراءة وإرسال)
tags.manage إدارة الوسوم
quick-replies.manage إدارة الردود السريعة
commerce.view قراءة الطلبات والمنتجات
commerce.manage إدارة الطلبات والمنتجات
campaigns.view قراءة الحملات
campaigns.manage التحكم بالحملات (إيقاف مؤقت، استئناف، إلغاء)
calendar.view قراءة الحجوزات
calendar.manage إدارة الحجوزات

يُصدَر المفتاح بالنطاقات التي تحدّدها بالضبط — بلا تعميم، وبما لا يتجاوز ما تملكه أنت.

غلاف الاستجابة

الاستجابة الناجحة تغلّف البيانات داخل مفتاح data، مع كتلة meta اختيارية (تستخدمها القوائم المصفّحة):

{
  "data": { ... },
  "meta": { "next_cursor": null, "has_more": false }
}

يُرجِع الفشل كائن خطأ موحّداً واحداً:

{
  "error": {
    "type": "invalid_request_error",
    "code": "not_found",
    "message": "The requested resource was not found.",
    "param": null,
    "request_id": "req_01J..."
  }
}

رموز الأخطاء

سلسلة code عقد ثابت مقروء آلياً (مستقلّ عن اللغة، لا يُعاد تسميته) لتفرّع منطقك عليه؛ أمّا الرسالة البشرية message فتُترجَم على حدة. أمّا type فهو عائلة الخطأ العامّة.

الرمزالنوعHTTP
bad_request invalid_request_error 400
validation_failed invalid_request_error 422
unauthenticated authentication_error 401
forbidden authorization_error 403
not_found invalid_request_error 404
method_not_allowed invalid_request_error 405
not_acceptable invalid_request_error 406
conflict idempotency_error 409
idempotency_conflict idempotency_error 409
payload_too_large invalid_request_error 413
rate_limited rate_limit_error 429
server_error api_error 500

كل خطأ يردّد request_id (نفس قيمة ترويسة X-Request-Id) — اذكره عند الإبلاغ عن مشكلة.

حدود المعدّل

تسمح الواجهة بـ120 طلباً في الدقيقة لكل مفتاح (الطلبات غير المصادَقة تُحدّ بعنوان IP). كل استجابة تحمل الترويسات القياسية:

  • X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After
  • تجاوز الحدّ يُرجِع 429 برمز rate_limited؛ تمهّل حتى Retry-After.

عدم التكرار (Idempotency)

أرسِل ترويسة Idempotency-Key مع أي POST أو PUT أو PATCH أو DELETE ليُطبَّق الطلب المُعاد مرّة واحدة على الأكثر. استخدم UUID جديداً لكل عملية منطقية:

Idempotency-Key: 6f9e0b2c-1a3d-4e5f-8a7b-9c0d1e2f3a4b
  • إعادة نفس المفتاح والجسم تُرجِع الاستجابة المخزّنة مع Idempotency-Replayed: true.
  • تُحفظ المفاتيح لمدّة 24 ساعة.
  • إعادة استخدام مفتاح بجسم مختلف، أو والطلب الأول ما زال جارياً، تُرجِع 409 idempotency_conflict.

التصفّح (Pagination)

القوائم تستخدم تصفّحاً مبهماً قائماً على المؤشّر (keyset). مرّر limit (افتراضياً 25، وأقصاه 100) ومؤشّراً cursor مبهماً:

GET https://console.widers.net/api/v1/contacts?limit=25&cursor=<opaque>

تحمل meta في الاستجابة مؤشّر الصفحة التالية وما إن كانت هناك صفوف أخرى. عندما يكون has_more قيمته false يكون next_cursor فارغاً null:

"meta": { "next_cursor": "eyJ...", "has_more": true }

المؤشّر التالف يُرجِع 422 — لا مسحاً كاملاً صامتاً. عامِل المؤشّر ككتلة مبهمة وأعده كما هو حرفياً.

وضع الاختبار (Sandbox)

وضع الاختبار يحمله المفتاح (مفتاح اختبار)، فلا تقلبه أثناء الطلب. طلب الـSandbox لا يمسّ عملاء أو طلبات أو أموالاً حقيقية — بل يصطنع الاستجابات:

  • كل استجابة تُعلن وضعها: X-Widers-Mode: live / X-Widers-Mode: test
  • مفتاح وضع الاختبار يتوقّف قبل أي أثر فعلي، فلا يُطلق أي Webhook — كل Webhook يصلك يكون من نشاط حقيقي.
  • الخدمات ذات الأثر الجانبي تصطنع نداءاتها الخارجية في وضع الاختبار.

الأحداث

تتلقّى نقطتك حدثاً كلّما وقع شيءٌ في الحساب. ولكلّ حدثٍ اسمٌ ثابتٌ بالنقطة — عقدٌ عامّ: مُؤصدَرٌ ولا يُعاد تسميته أبداً. وينتمي كلّ حدثٍ إلى نطاق، فلا يشترك المفتاح في حدثٍ ولا يتلقّاه إلّا إن سمحت به نطاقاته. اشترِك من الإعدادات ← Webhooks.

الحدثما هوالمجموعةالنطاق
flow.completed اكتمل التدفق الأتمتة —
flow.handoff_requested طلب تحويل لموظف الأتمتة —
conversation.csat_received وصل تقييم رضا الأتمتة —
campaign.completed اكتملت الحملة الحملات campaigns.view
campaign.failed فشلت الحملة الحملات campaigns.view
order.created طلب جديد الطلبات commerce.view
order.paid دفع الطلب الطلبات commerce.view
order.shipped شحن الطلب الطلبات commerce.view
order.ready_for_pickup الطلب جاهز للاستلام الطلبات commerce.view
order.delivered تسليم الطلب الطلبات commerce.view
order.cancelled إلغاء الطلب الطلبات commerce.view
order.refunded استرداد الطلب الطلبات commerce.view
cart.abandoned سلة متروكة الطلبات commerce.view
product.created منتج جديد الطلبات commerce.view
product.updated تحديث منتج الطلبات commerce.view
invoice.paid دُفعت الفاتورة الفوترة —
booking.created إنشاء حجز ويب هوك الحجوزات calendar.view
booking.updated تعديل حجز ويب هوك الحجوزات calendar.view
booking.cancelled إلغاء حجز ويب هوك الحجوزات calendar.view
booking.confirmed تأكيد حجز ويب هوك الحجوزات calendar.view
message.received وصول رسالة ويب هوك الرسائل inbox.access
message.sent إرسال رسالة ويب هوك الرسائل inbox.access
conversation.created إنشاء محادثة ويب هوك الرسائل inbox.access
conversation.assigned إسناد محادثة ويب هوك الرسائل inbox.access
conversation.transferred تحويل محادثة ويب هوك الرسائل inbox.access
conversation.closed إغلاق محادثة ويب هوك الرسائل inbox.access
conversation.reopened إعادة فتح محادثة ويب هوك الرسائل inbox.access
conversation.muted كتم محادثة ويب هوك الرسائل inbox.access
conversation.unmuted إلغاء كتم محادثة ويب هوك الرسائل inbox.access
conversation.archived أرشفة محادثة ويب هوك الرسائل inbox.access
conversation.unarchived استعادة محادثة ويب هوك الرسائل inbox.access
conversation.tagged إضافة وسم لمحادثة ويب هوك الرسائل inbox.access
conversation.untagged إزالة وسم من محادثة ويب هوك الرسائل inbox.access
message.delivered تسليم رسالة ويب هوك الرسائل inbox.access
message.read قراءة رسالة ويب هوك الرسائل inbox.access
message.failed فشل رسالة ويب هوك الرسائل inbox.access
conversation.unassigned إلغاء إسناد محادثة ويب هوك الرسائل inbox.access
contact.created إنشاء جهة اتصال ويب هوك جهات الاتصال contacts.view
contact.updated تعديل جهة اتصال ويب هوك جهات الاتصال contacts.view
contact.blocked حظر جهة اتصال ويب هوك جهات الاتصال contacts.view
contact.unblocked رفع الحظر عن جهة اتصال ويب هوك جهات الاتصال contacts.view
contact.tagged إضافة وسم لجهة اتصال ويب هوك جهات الاتصال contacts.view
contact.untagged إزالة وسم من جهة اتصال ويب هوك جهات الاتصال contacts.view
contact.deleted حذف جهة اتصال ويب هوك جهات الاتصال contacts.view

يَسرُد GET /events الأحداثَ التي يجوز لمفتاحك الاشتراك فيها؛ ويُرجِع GET /events/{event}/samples أحدثَ مغلّفات تسليمٍ حقيقيّة لحدثٍ واحد، فتبني وتختبر على شكله الدقيق.

توقيع الـWebhook

كل webhook نرسله إلى نقطتك موقّع بـHMAC مختوم بالوقت في ترويسة X-Widers-Signature، لتتحقّق من الأصالة والحداثة معاً:

X-Widers-Signature: t=1706342400,v1=<hmac_sha256>
  • تحقّق مقابل جسم الطلب الخام غير المحلَّل — احسب hmac_sha256(secret, "<t>.<body>").
  • ارفض أي طلب تبعد طابعه الزمني أكثر من 300 ثانية عن الآن (دفاعاً عن إعادة التشغيل).
  • قارِن بدالّة ثابتة الزمن (hash_equals) تفادياً لهجمات التوقيت.
  • أثناء تدوير السرّ تحمل الترويسة أكثر من v1 (السرّ الجديد والسابق). اقبل الطلب إن طابق أيّ v1 — والمُتحقّق أدناه يفعل ذلك.

مُتحقّق جاهز للنسخ (PHP):

<?php

function widers_verify(string $secret, string $rawBody, string $header): bool
{
    // Parse "t=<unix>,v1=<hex>[,v1=<hex>…]" — there may be MORE THAN ONE v1 while a secret is rotating.
    $t = 0;
    $signatures = [];
    foreach (explode(',', $header) as $part) {
        [$key, $value] = array_pad(explode('=', trim($part), 2), 2, '');
        if ($key === 't') {
            $t = (int) $value;
        } elseif ($key === 'v1') {
            $signatures[] = $value;
        }
    }

    if ($t <= 0 || abs(time() - $t) > 300) {
        return false; // missing/stale timestamp — outside the 300s replay window
    }

    $expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
    foreach ($signatures as $signature) { // accept ANY matching v1, so rotation never drops a delivery
        if (hash_equals($expected, $signature)) {
            return true;
        }
    }

    return false;
}

الفحص نفسه بلغة Node.js:

const crypto = require('crypto');

function widersVerify(secret, rawBody, header) {
  let t = 0;
  const sigs = [];
  for (const part of header.split(',')) {
    const [k, v = ''] = part.trim().split('=');
    if (k === 't') t = parseInt(v, 10);
    else if (k === 'v1') sigs.push(v);
  }
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  return sigs.some((s) => s.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected)));
}

وبلغة Python:

import hashlib, hmac, time

def widers_verify(secret: str, raw_body: str, header: str) -> bool:
    t, sigs = 0, []
    for part in header.split(","):
        k, _, v = part.strip().partition("=")
        if k == "t":
            t = int(v or 0)
        elif k == "v1":
            sigs.append(v)
    if t <= 0 or abs(time.time() - t) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{t}.{raw_body}".encode(), hashlib.sha256).hexdigest()
    return any(hmac.compare_digest(expected, s) for s in sigs)

تسليم الـWebhook

يُسلَّم كلّ حدثٍ عبر POST بهذا المغلّف JSON. فرّع على type، وراعِ api_version لتبقى متوافقاً حين يكتسب المغلّف حقولاً مستقبلاً:

POST  https://your-endpoint.example/webhooks
Content-Type: application/json
X-Widers-Signature: t=1706342400,v1=<hmac_sha256>
X-Widers-Delivery: evt_01J...

{
  "id": "evt_01J...",
  "type": "order.paid",
  "api_version": "v1",
  "created_at": "2026-10-30T12:00:00+00:00",
  "data": { ... }
}
  • التسليم «مرّةً على الأقلّ» — قد يصل الحدث نفسه أكثر من مرّة. والمعرّف id في المغلّف (ويتكرّر في ترويسة X-Widers-Delivery) ثابتٌ لكلّ تسليم: نقِّ التكرار به. والترتيب غير مضمون؛ رتّب بحسب created_at عند الحاجة.
  • أيّ تسليمٍ لا يُعيد 2xx خلال 10 ثوانٍ — أو يُعيد 4xx/5xx — يُعاد بتراجعٍ أُسّيّ: بعد دقيقة، ثمّ 10 دقائق، ثمّ ساعة، ثمّ 6 ساعات، ثمّ 24 ساعة (ستّ محاولاتٍ إجمالاً).
  • بعد 15 تسليماً فاشلاً متتالياً يتعطّل الاشتراك تلقائيّاً — حمايةً لنقطتك ولنا — ويُوسَم في لوحة التحكّم. أصلِح نقطتك ثمّ أعِد تفعيله من هناك؛ وإعادة التفعيل تُصفّر سلسلة الإخفاق.
  • أعِد 2xx فور تخزينك الحدث، ثمّ عالِجه لا تزامنيّاً. فالمُعالِج البطيء تنفد مهلته فيُطلق إعاداتٍ لا لزوم لها.
  • دوّر سرّ النقطة من لوحة التحكّم وقتما شئت: يبقى السرّ السابق صالحاً خلال فترة سماحٍ مدّتها 24 ساعة (يُرسَل الاثنان، كلٌّ كـv1 مستقلّ)، فتُحدِّث سرّك المخزّن قبل انغلاقها.

أنشئ اشتراكاً من لوحة التحكّم (الإعدادات ← Webhooks) أو عبر الـAPI — ويُعرَض سرّ التوقيع مرّةً واحدةً عند الإنشاء:

curl -X POST https://console.widers.net/api/v1/webhooks \
  -H "Authorization: Bearer wa_live_<id>|<token>" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://your-endpoint.example/webhooks","topics":["order.paid","message.failed"]}'

إطلاق تدفّق

يمكن للتدفّق أن يكشف عنوان Webhook موقّعاً خاصّاً به — أضِف مُحفّز «Webhook وارد» في باني التدفّقات ليُولّده. أرسِل JSON بـPOST إليه فيعمل التدفّق على جهة الاتصال المُستنتَجة من الحمولة، وتُتاح له تلك الحمولة بياناتٍ.

POST https://console.widers.net/flow/webhook/<token>

صادِق بسرّ المُحفّز بأيّ الطريقتين — اختَر الأسهل على مُنادِيك:

  • كتوكن حامل (مُوصى به): Authorization: Bearer <secret> — أو ترويسة X-Widers-Token. السرّ هو بيان الاعتماد، كمفتاح API؛ لا شيء يُحسَب.
  • أو بنفس توقيع الـwebhooks الصادرة: X-Widers-Signature: t=<unix>,v1=<hmac_sha256(secret, "t.body")> — فيعمل مُوقِّعٌ/مُتحقِّقٌ واحدٌ في الاتّجاهين.
curl -X POST https://console.widers.net/flow/webhook/<token> \
  -H "Authorization: Bearer <trigger_secret>" \
  -H "Content-Type: application/json" \
  -d '{"phone":"+9665XXXXXXXX","name":"..."}'

الاستجابات: 202 عند النجاح؛ و422 إن تعذّر استنتاج جهة اتصالٍ أو محادثةٍ من الحمولة؛ و401 لسرٍّ أو توقيعٍ خاطئ؛ و404 لمُحفّزٍ مجهولٍ أو مُعطَّل؛ و413 أو 415 أو 429 لجسمٍ ضخمٍ أو غير JSON أو لكثرة الطلبات.

طلبك الأول

أسرع تحقّقٍ من أنّ المفتاح يعمل: يُرجِع GET /me الحسابَ والاتّصال الذي يعمل عليه المفتاح. ولا يتطلّب أيّ نطاق، فهو المناداة الأولى المثلى:

curl https://console.widers.net/api/v1/me \
  -H "Authorization: Bearer wa_live_<id>|<token>" \
  -H "Accept: application/json"

ثمّ اسرد جهات اتصالك بطلب واحد مصادَق:

curl https://console.widers.net/api/v1/contacts \
  -H "Authorization: Bearer wa_live_<id>|<token>" \
  -H "Accept: application/json"

حجوزات التقويم (مصادقة قديمة)

حجوزات التقويم سبقت طقم الـAPI هذا وتحتفظ بعقدها الخاص. وفيما يلي الفروق.
  • يصادِق بصلاحيات Sanctum القديمة، لا بالنطاقات أعلاه: bookings.read / bookings.write
  • يُرجِع غلاف Laravel الافتراضي، لا شكل data/error: {"data":[...]}
  • لا تصفّح بالمؤشّر، ولا وضع اختبار، ولا دعم لعدم التكرار — قائمته غير مصفّحة ومحدودة بسقف صارم.

سجلّ التغييرات

إضافاتٌ متوافقةٌ مع السابق فقط — قد تظهر حقولٌ جديدة، فحلّل بتسامح. وأيّ تغييرٍ كاسرٍ يصدر تحت api_version جديد مع إبقاء v1 جارياً، ويُعلَن عن أيّ إهمالٍ هنا مسبقاً.

  • 2026-10-30 — الإرسال من قناةٍ محدّدة: تقبل نقطة الإرسال مُعرّف connection، ويَسرُد GET /connections أرقامك المتّصلة وحالتها.
  • 2026-10-30 — كتالوج الأحداث: يَسرُد GET /events الأحداثَ التي يجوز للمفتاح الاشتراك فيها، ويُرجِع GET /events/{event}/samples مغلّفاتٍ حقيقيّةً حديثة.
  • 2026-10-30 — صارت مغلّفات الـwebhook تحمل حقل api_version، ويقبل مُحفّز التدفّق الوارد نفس توقيع t=,v1= للـwebhooks الصادرة.
  • 2026-10-30 — تتعطّل اشتراكات الـwebhook تلقائيّاً بعد إخفاقات تسليمٍ متكرّرة، ويمكن إعادة تفعيلها من لوحة التحكّم.
  • 2026-10-30 — CORS: صارت ترويسات حدود المعدّل وعدم التكرار مكشوفةً لعملاء المتصفّح.

مستعدّ لاستكشاف كل نقطة نهاية؟ راجِع مرجع الـAPI