Englishابدأ مجاناً
ابدأ التجربة المجانية ←English
التوثيق
Scale API

دليل تكامل Scale API

اربط نظام CRM أو ERP أو متجرك أو نظام الحجوزات أو تطبيقك بـ WhatsApp عبر Scale: المصادقة، وربط WhatsApp، وإرسال الرسائل والقوالب، وحالة الرسائل، والـ Webhooks الموقّعة، والاستخدام، وحدود معدل الطلبات، والأخطاء — مع أمثلة كاملة.

SC بواسطة Scale Team آخر تحديث 24 Sep 2026 23 دقائق قراءة
من يستطيع تنفيذها
المطوّرون

يأخذ هذا الدليل أي نظام خارجي — نظام CRM أو ERP أو متجر إلكتروني أو نظام حجوزات أو الخادم الخلفي لموقع ويب أو أي تطبيق مخصص — من الصفر إلى تكامل WhatsApp يعمل فعليًا مع Scale: إرسال الرسائل، ومتابعة تسليمها، واستقبال الردود، والاستجابة لتغيّرات حالة الاتصال. كل مثال في هذا الدليل هو الشكل الدقيق الذي تقبله واجهة Scale API وتعيده.

باختصار: يتصل خادمك بواجهة Scale API باستخدام مفتاح API من Scale، وتتولى Scale التواصل مع WhatsApp نيابةً عنك. لا تحتاج أبدًا إلى رمز وصول (Access Token) من Meta أو إلى سرّ التطبيق (App Secret) أو إلى كلمة مرور WhatsApp Business.

البدء

باستخدام واجهة Scale API يستطيع نظامك الخارجي:

  • إرسال رسائل WhatsApp من مساحة العمل (Workspace) الخاصة بك في Scale: نصوص، وقوالب معتمدة، وصور، وفيديو، وصوت، ومستندات؛
  • معرفة حالة كل رسالة أرسلها (pending و sent و delivered و read و failed و unknown)؛
  • استقبال Webhooks موقّعة عندما يكتب العميل، وعندما تُرسَل رسالة أو تُسلَّم أو تُقرأ أو تفشل، وعندما يتم ربط WhatsApp أو فصله؛
  • تمكين العميل من ربط رقم WhatsApp الخاص به عبر صفحة تستضيفها Scale؛
  • قراءة حالة اتصال WhatsApp لمساحة العمل واستهلاكها من الرسائل.
كيف تترابط الأجزاء
Your system (CRM, ERP, store, app)
        │   HTTPS + Authorization: Bearer sk_live_…
        ▼
Scale Public API   (https://getscale.me/wp-json/scale/v1/)
        │
        ▼
Scale WhatsApp     (your workspace, its Inbox, its allowance)
        │
        ▼
Meta WhatsApp Cloud API

نظامك لا يتحدث إلا مع Scale. اتصال WhatsApp وبيانات اعتماد Meta الخاصة به والتواصل مع Meta تبقى كلها داخل Scale، ولا تعيدها الواجهة أبدًا. كل ما يرسله نظامك عبر الواجهة يظهر أيضًا في صندوق الوارد (Inbox) في Scale كجزء من المحادثة نفسها.

المتطلبات الأساسية

ما تحتاجهمن أين تحصل عليه
مساحة عمل (Workspace) في Scale باشتراك فعّالحسابك في Scale. رسائل الواجهة تستخدم رصيد الرسائل نفسه المخصص لبقية مساحة العمل.
تفعيل الوصول إلى Public API لمساحة العملتفعّله Scale لمساحة عملك. بدونه يُرفض كل طلب إلى الواجهة بالرد 403 API_ACCESS_DISABLED ولا تظهر صفحة Developer & API.
مفتاح APIلوحة التحكم ← Settings‏ ← Developer & API‏ ← API keys‏ ← Create API key. يُعرض المفتاح مرة واحدة فقط.
رقم WhatsApp مربوطلوحة التحكم ← Settings‏ ← Channels، أو عبر الربط المستضاف (Onboarding) الموضّح أدناه.
رابط Webhook (اختياري، ويُنصح به)نقطة نهاية https:// خاصة بك، تحفظها في Settings‏ ← Developer & API‏ ← Webhook.
رابط عودة للربط (اختياري)Settings‏ ← Developer & API‏ ← Onboarding return URLs — فقط إذا كنت تستخدم الربط المستضاف مع إعادة التوجيه إلى موقعك.
HTTPS فقط. روابط الـ Webhook وروابط العودة الخاصة بالربط وروابط الوسائط يجب أن تكون كلها عناوين https:// على الإنترنت العام.

الرابط الأساسي (Base URL)

الرابط الأساسي
https://getscale.me/wp-json/scale/v1/

كل نقطة نهاية (Endpoint) في هذا الدليل نسبية إليه: POST /messages تعني POST https://getscale.me/wp-json/scale/v1/messages. الطلبات والردود بصيغة JSON‏ (Content-Type: application/json).

المصادقة (Authentication)

أرسل مفتاح API في الترويسة Authorization مع كل طلب:

الترويسة (Header)
Authorization: Bearer sk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  • المفتاح هو sk_live_ متبوعًا بـ 40 حرفًا ورقمًا. ينتمي إلى مساحة عمل واحدة فقط، وكل ما يفعله يحدث داخل تلك المساحة.
  • في الترويسة فقط. المفتاح الموضوع في الرابط (?api_key= أو ?key= أو ?token= أو ?access_token= أو ?apikey=) يُرفض بالرد 401 INVALID_API_KEY حتى لو وُجدت ترويسة صحيحة أيضًا — ألغِ (Revoke) أي مفتاح وُضع في رابط ولو مرة واحدة.
  • على الخادم فقط. لا تضع المفتاح أبدًا في JavaScript يعمل في المتصفح، أو في تطبيق جوال، أو في أي شيء يمكن للعميل تنزيله. احفظه في مخزن الأسرار على خادمك.
  • يُعرض مرة واحدة. تحتفظ Scale ببصمة (Hash) للمفتاح فقط؛ بعد إنشائه لا ترى إلا أحرفه الأولى (sk_live_xxxxxx…). إذا فقدته، أنشئ مفتاحًا جديدًا وألغِ القديم.
  • يمكن أن يكون لمساحة العمل حتى 20 مفتاحًا فعّالًا. يمكن لكل نظام أن يحصل على مفتاحه الخاص، وتتشارك المفاتيح حدّ معدل الطلبات ورصيد الرسائل الخاصين بمساحة العمل.
أول طلب لك
curl https://getscale.me/wp-json/scale/v1/usage \
  -H "Authorization: Bearer sk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

كل رد يكون بصيغة JSON. الرد الناجح يحتوي على "success": true مع بياناته؛ أما الفشل فله الشكل نفسه في كل مكان:

غلاف الخطأ (Error envelope)
{
  "success": false,
  "error": {
    "code": "INVALID_API_KEY",
    "message": "The API key is missing or invalid.",
    "details": {}
  },
  "request_id": "req_k2V9pQ7sL1mN4xR8tY3wZ6aB"
}

كل رد يحمل الترويسة X-Scale-Request-Id بمعرّف req_… نفسه — اذكره إذا تواصلت مع الدعم. والردود التي تتجاوز مرحلة المصادقة تحمل أيضًا X-RateLimit-Limit و X-RateLimit-Remaining و X-RateLimit-Reset (راجع حدود معدل الطلبات).

البدء السريع

  1. أنشئ مفتاح API — من Settings‏ ← Developer & API‏ ← API keys. انسخه إلى مخزن الأسرار على خادمك.
  2. اربط WhatsApp — من Settings‏ ← Channels في لوحة التحكم، أو أرسل عميلك عبر الربط المستضاف (القسم التالي). تحقّق عبر GET /whatsapp/status: القيمة "can_send": true تعني أنك جاهز.
  3. اضبط الـ Webhook — من Settings‏ ← Developer & API‏ ← Webhook: أدخل رابط https:// الخاص بك. انسخ سرّ التوقيع (whsec_…) الذي يُعرض مرة واحدة، ثم اضغط "Send test event".
  4. أرسل أول رسالة — POST /messages مع Idempotency-Key.
  5. استقبل أحداث الـ Webhook‏ — message.sent ثم message.delivered و message.read؛ و message.received عندما يرد العميل.
  6. تحقّق من حالة الرسالة — GET /messages/{id} باستخدام معرّف msg_… الذي حصلت عليه.
الخطوة 2 — هل WhatsApp جاهز؟
curl https://getscale.me/wp-json/scale/v1/whatsapp/status \
  -H "Authorization: Bearer $SCALE_API_KEY"
الخطوة 4 — إرسال رسالة نصية
curl -X POST https://getscale.me/wp-json/scale/v1/messages \
  -H "Authorization: Bearer $SCALE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-123-message-1" \
  -d '{"to":"201012345678","type":"text","text":{"body":"Hello from our store! Your order #123 is confirmed."}}'
الرد — 200
{
  "success": true,
  "message": {
    "id": "msg_Q2w3E4r5T6y7U8i9O0p1A2s3",
    "wamid": "wamid.HBgMMjAxMDEyMzQ1Njc4FQIAERgSQzE5",
    "status": "accepted",
    "type": "text",
    "to": "201012345678",
    "created_at": "2026-09-24T10:00:00Z"
  },
  "replayed": false
}
الخطوة 6 — حالتها
curl https://getscale.me/wp-json/scale/v1/messages/msg_Q2w3E4r5T6y7U8i9O0p1A2s3 \
  -H "Authorization: Bearer $SCALE_API_KEY"
النص الحر يحتاج إلى محادثة مفتوحة. لا يسمح WhatsApp بإرسال النصوص والوسائط إلا خلال 24 ساعة من آخر رسالة أرسلها العميل إليك. لبدء محادثة جديدة، أرسل قالبًا معتمدًا (راجع القوالب).

ربط WhatsApp‏ (Onboarding)

يتيح الربط المستضاف (Hosted Onboarding) لمساحة العمل ربط رقم WhatsApp الخاص بها دون لوحة التحكم: يطلب نظامك من Scale رابطًا لمرة واحدة، يفتحه الشخص، ويُكمل تسجيل Meta على صفحة تستضيفها Scale، ثم يُعاد إلى موقعك. تسجيل Meta يعمل على Scale — ولا تصل بيانات اعتماد Meta إلى نظامك أبدًا.

  1. (اختياري) سجّل رابط العودة: Settings‏ ← Developer & API‏ ← Onboarding return URLs.
  2. يستدعي خادمك POST /whatsapp/onboarding/session.
  3. أرسل الشخص إلى onboarding_url الوارد في الرد (بإعادة توجيه المتصفح أو برابط).
  4. يُكمل الشخص Embedded Signup الخاص بـ Meta على صفحة Scale (ويُدخل رمز PIN الخاص بـ WhatsApp المكوّن من 6 أرقام إذا طلبه الرقم).
  5. تعيد Scale متصفحه إلى return_url الخاص بك مع النتيجة — وإذا كنت قد ضبطت Webhook، ترسل أيضًا whatsapp.connected.
  6. يتأكد خادمك باستخدام GET /whatsapp/onboarding/session/{id} أو GET /whatsapp/status.

بدء جلسة

POST /whatsapp/onboarding/session
curl -X POST https://getscale.me/wp-json/scale/v1/whatsapp/onboarding/session \
  -H "Authorization: Bearer $SCALE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"return_url":"https://crm.example.com/scale/connected","state":"crm-account-42"}'
الحقلإلزاميالمعنى
return_urlلاالمكان الذي تعيد Scale المتصفح إليه في النهاية. يجب أن يطابق تمامًا رابطًا مسجّلًا في Settings‏ ← Developer & API‏ ← Onboarding return URLs، وإلا يكون الرد 422 INVALID_RETURN_URL. بدونه تخبر الصفحةُ الشخصَ بأنه يستطيع إغلاقها.
stateلامرجعك الخاص، حتى 200 حرف ASCII مرئي. يُعاد إليك على رابط العودة.
الرد — 201
{
  "success": true,
  "session_id": "onb_Z1x2C3v4B5n6M7a8S9d0F1g2",
  "onboarding_url": "https://getscale.me/?scale_onboarding=1#t=Hk3mP9sQ2vX7yB4nC8dF1gJ6kL0zR5tW3eA9uI2oN7pM4qS8",
  "expires_at": "2026-09-24T10:30:00Z",
  "return_url": "https://crm.example.com/scale/connected"
}
  • الرابط صالح لمدة 30 دقيقة ويُفتح مرة واحدة: أول متصفح يفتحه يرتبط بالجلسة؛ وفتحه مرة أخرى يعرض "This link has already been used".
  • لا توجد إلا جلسة واحدة نشطة لكل مساحة عمل: إنشاء جلسة جديدة يلغي أي جلسة سابقة لم تكتمل.
  • إذا كان لدى مساحة العمل اتصال WhatsApp يعمل بالفعل، يكون الرد 409 WHATSAPP_ALREADY_CONNECTED — افصل الاتصال في Scale أولًا لربط رقم مختلف.
  • 503 ONBOARDING_UNAVAILABLE تعني أن الربط غير متاح على نسخة Scale هذه في الوقت الحالي.

روابط العودة (Return URLs)

القاعدةالتفاصيل
HTTPShttps:// فقط، واسم نطاق عام، وبحد أقصى 500 حرف.
تطابق تاميجب أن يكون return_url في الطلب مطابقًا حرفًا بحرف لأحد الروابط المسجّلة.
غير مسموحبيانات دخول (user:pass@)، أو #fragment، أو استعلام يحتوي أصلًا على session_id أو status أو state أو error — فـ Scale تضيفها بنفسها.
العددحتى 10 روابط لكل مساحة عمل.

عندما ينتهي الشخص، أو يلغي، أو يفشل الاتصال، تعيد Scale توجيه المتصفح إلى رابط العودة الخاص بك مع إضافة معاملات الاستعلام التالية (تُحذف المعاملات الفارغة):

إعادة التوجيه
https://crm.example.com/scale/connected?session_id=onb_Z1x2C3v4B5n6M7a8S9d0F1g2&status=connected&state=crm-account-42
https://crm.example.com/scale/connected?session_id=onb_Z1x2C3v4B5n6M7a8S9d0F1g2&status=failed&state=crm-account-42&error=meta_pin_required
تعامَل مع إعادة التوجيه كإشارة، لا كدليل. يمكن لأي شخص كتابة رابط. تأكّد من النتيجة من خادمك باستخدام GET /whatsapp/onboarding/session/{id} قبل الاعتماد عليها.

التحقق من جلسة

GET /whatsapp/onboarding/session‏/{id}
curl https://getscale.me/wp-json/scale/v1/whatsapp/onboarding/session/onb_Z1x2C3v4B5n6M7a8S9d0F1g2 \
  -H "Authorization: Bearer $SCALE_API_KEY"
الرد — 200
{
  "success": true,
  "session_id": "onb_Z1x2C3v4B5n6M7a8S9d0F1g2",
  "status": "connected",
  "error": null,
  "return_url": "https://crm.example.com/scale/connected",
  "created_at": "2026-09-24T10:00:00Z",
  "expires_at": "2026-09-24T10:30:00Z",
  "completed_at": "2026-09-24T10:04:12Z",
  "whatsapp": {
    "phone_number_id": "109876543210987",
    "waba_id": "123456789012345"
  }
}
statusالمعنى
createdأُنشئت الجلسة، ولم يُفتح الرابط بعد.
openedفُتح الرابط؛ تسجيل Meta قيد التنفيذ.
connectingتُكمل Scale الاتصال مع Meta.
pin_requiredيحتاج الرقم إلى رمز PIN الخاص بـ WhatsApp المكوّن من 6 أرقام؛ والصفحة تطلبه.
connectedتم. يحتوي whatsapp على Phone Number ID و WABA ID.
failedلم يتم الربط؛ يحتوي error على رمز السبب.
cancelledألغى الشخص العملية، أو حلّت محلها جلسة أحدث (error‏: superseded).
expiredانقضت الـ 30 دقيقة قبل الاكتمال.

الجلسة تنتمي إلى مساحة عملها: معرّف جلسة من مساحة عمل أخرى يعيد 404 ONBOARDING_SESSION_NOT_FOUND.

إرسال رسائل WhatsApp

POST /messages ترسل رسالة واحدة إلى مستلم واحد. يحتوي جسم الطلب دائمًا على to و type، بالإضافة إلى كائن واحد يحمل اسم النوع.

الحقلالقاعدة
toالرقم الدولي الكامل للمستلم، مع رمز الدولة: 201012345678. تُقبل العلامة + أو 00 في البداية والمسافات والشرطات، وتُحذف؛ من 8 إلى 15 رقمًا. الرقم المحلي بدون رمز الدولة لا يُحوَّل.
typetext أو template أو image أو video أو audio أو document.
الترويسة Idempotency-Keyاختيارية لكن يُنصح بها بشدة — راجع منع التكرار.
  • نافذة الـ 24 ساعة. لا تُقبل رسائل text والوسائط إلا خلال 24 ساعة من آخر رسالة أرسلها العميل إلى رقمك؛ وإلا يكون الرد 422 OUTSIDE_MESSAGING_WINDOW مع details.alternative: "template". يمكن إرسال القوالب في أي وقت، والقالب المرسل إلى رقم جديد يُنشئ أيضًا المحادثة في Scale.
  • رسالة واحدة في كل مرة لكل مساحة عمل. ترسل Scale رسائل الواجهة الخاصة بمساحة العمل واحدة تلو الأخرى. الطلب الذي يصل أثناء إرسال رسالة أخرى ينتظر حتى ثانية واحدة، ثم يرد بـ 429 RATE_LIMITED مع details.reason: "send_in_progress" و Retry-After: 1 — أعد المحاولة (بالقيمة نفسها لـ Idempotency-Key).
  • معنى النجاح. الرد 200 مع "status": "accepted" يعني أن WhatsApp قبل الرسالة وأعاد معرّفها (wamid). التسليم والقراءة يأتيان لاحقًا — عبر الـ Webhooks أو GET /messages/{id}.
  • إنها رسالة محادثة عادية. تظهر الرسالة في Inbox الخاص بـ Scale، وتُخصم رسالة واحدة من رصيد مساحة العمل.

نص (text)

الطلب
{
  "to": "201012345678",
  "type": "text",
  "text": { "body": "Your appointment is confirmed for Thursday at 4 pm." }
}

text.body إلزامي، وبحد أقصى 4,000 حرف.

قالب (template)

الطلب
{
  "to": "201012345678",
  "type": "template",
  "template": {
    "name": "order_shipped",
    "language": "ar",
    "components": [
      {
        "type": "body",
        "parameters": [
          { "type": "text", "text": "Ahmed" },
          { "type": "text", "text": "#123" }
        ]
      }
    ]
  }
}

template.name و template.language إلزاميان؛ و template.components اختياري. التفاصيل في قسم القوالب.

صورة (image)

الطلب
{
  "to": "201012345678",
  "type": "image",
  "image": { "link": "https://cdn.example.com/products/sku-778.jpg", "caption": "Your new bag" }
}

فيديو (video)

الطلب
{
  "to": "201012345678",
  "type": "video",
  "video": { "link": "https://cdn.example.com/howto/setup.mp4", "caption": "How to set it up" }
}

صوت (audio)

الطلب
{
  "to": "201012345678",
  "type": "audio",
  "audio": { "link": "https://cdn.example.com/voice/welcome.ogg" }
}

مستند (document)

الطلب
{
  "to": "201012345678",
  "type": "document",
  "document": {
    "link": "https://cdn.example.com/invoices/INV-2026-0042.pdf",
    "filename": "INV-2026-0042.pdf",
    "caption": "Your invoice"
  }
}
النوعإلزامياختياريالحدود
texttext.body—4,000 حرف
templatetemplate.name و template.languagetemplate.componentsحتى 20 مكوّنًا
imageimage.linkimage.captionالتعليق حتى 1,000 حرف
videovideo.linkvideo.captionالتعليق حتى 1,000 حرف
audioaudio.link—الصوت لا يقبل تعليقًا
documentdocument.linkdocument.filename و document.captionالتعليق حتى 1,000 حرف

كل الأنواع ترد بشكل النجاح نفسه الموضّح في البدء السريع. الرسالة التي تعذّر إرسالها ترد بخطأ (راجع الأخطاء)؛ والخطأ META_API_ERROR هو الذي يجب قراءته بعناية.

القوالب (Templates)

القالب رسالة اعتمدتها Meta مسبقًا لحساب WhatsApp Business الخاص بك. وهو النوع الوحيد من الرسائل الذي يسمح WhatsApp بإرساله خارج نافذة الـ 24 ساعة — كأول تواصل، أو تحديث لطلب، أو تذكير.

  • name — اسم القالب المعتمد تمامًا كما هو لدى Meta: أحرف إنجليزية صغيرة وأرقام وشرطات سفلية (order_shipped).
  • language — رمز لغة WhatsApp للترجمة المعتمدة: "ar" أو "en" أو "en_US". الصيغة { "code": "en_US" } مقبولة أيضًا.
  • components — الأجزاء المتغيرة، وتُمرَّر إلى Meta تمامًا كما ترسلها. كل مكوّن كائن قيمة type فيه هي header أو body أو button أو footer؛ و parameters الخاصة به تتبع صيغة رسائل القوالب لدى Meta. احذف components للقالب الذي لا يحتوي على متغيرات.
  • يجب أن يكون القالب معتمدًا لحساب WhatsApp Business المربوط بمساحة العمل. تتحقق Meta من الاسم واللغة والمعاملات: القالب غير الموجود أو غير المعتمد أو الذي يحصل على عدد خاطئ من المعاملات ترفضه Meta‏ — 502 META_API_ERROR مع details.outcome: "refused"، ولا يُرسَل شيء ولا يُخصم شيء.
  • القالب المرسل عبر الواجهة يأخذ رسالة واحدة من رصيد مساحة العمل، مثل أي رسالة عبر الواجهة. رسوم WhatsApp الخاصة بـ Meta منفصلة، وتفوترها Meta على حساب WhatsApp Business المربوط.
قالب مع صورة في الترويسة ومتغير في النص
{
  "to": "201012345678",
  "type": "template",
  "template": {
    "name": "new_collection",
    "language": { "code": "en_US" },
    "components": [
      { "type": "header", "parameters": [ { "type": "image", "image": { "link": "https://cdn.example.com/banner.jpg" } } ] },
      { "type": "body", "parameters": [ { "type": "text", "text": "Sara" } ] }
    ]
  }
}

رسائل الوسائط (Media)

  • أنت من يستضيف الملف. لا ترفع Scale الوسائط التي ترسلها ولا تخزّنها: يجب أن يكون link رابط https:// عامًا، وتقوم Meta بتنزيل الملف منه. أي ملف خلف تسجيل دخول، أو شبكة VPN، أو توقيع تنتهي صلاحيته قبل الأوان سيفشل.
  • تتحقق Scale فقط من أن link رابط https:// صالح. نوع الملف وحجمه يتحقق منهما WhatsApp؛ والملف الذي لا يستطيع استخدامه يُرفض (502 META_API_ERROR) أو يُبلَّغ عنه لاحقًا كـ message.failed.
  • caption مقبول للصورة والفيديو والمستند (حتى 1,000 حرف)؛ والصوت لا يقبل تعليقًا. document.filename هو الاسم الذي يراه العميل.
  • الوسائط رسائل حرة: لا تُرسل إلا داخل نافذة الـ 24 ساعة.

الوسائط التي يرسلها إليك عملاؤك تصل بشكل مختلف — راجع message.received: تخزّن Scale الصور والمقاطع الصوتية والمستندات، ويحمل الـ Webhook رابطًا إلى نسخة Scale منها.

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

الشبكات تفشل. قد يصل الطلب إلى Scale، وتُرسَل الرسالة، ثم يضيع الرد في طريق العودة — وإذا أعاد نظامك المحاولة ببساطة، يستلم العميل الرسالة مرتين. الترويسة Idempotency-Key تمنع ذلك.

الترويسة (Header)
Idempotency-Key: order-123-message-1
  • اختيارية، لكن استخدمها مع كل إرسال. بدونها يكون كل طلب رسالة جديدة، وإعادة المحاولة بعد انتهاء المهلة تصبح رسالة مكررة.
  • من 1 إلى 150 حرف ASCII مرئي (وإلا 400 INVALID_MESSAGE). اجعلها تصف الرسالة لا المحاولة: order-123-message-1، وليس قيمة عشوائية لكل محاولة. المفاتيح خاصة بكل مساحة عمل.
  • المفتاح نفسه والطلب نفسه ← الرد الأصلي: معرّف msg_… نفسه، و "replayed": true، ولا رسالة ثانية ولا خصم ثانٍ — حتى بعد ساعات.
  • المفتاح نفسه وطلب مختلف ← 409 IDEMPOTENCY_CONFLICT، مع details.message_id الذي يشير إلى الرسالة الأصلية. لا يُرسَل شيء.
  • تتم مقارنة الطلب بعد التحقق من صحته، لذا فالرسالة نفسها المكتوبة بمسافات JSON مختلفة أو بترتيب مفاتيح مختلف تُعد الطلب نفسه.
  • المفتاح نفسه بينما الطلب الأول ما زال قيد الإرسال ← 429 RATE_LIMITED‏ (send_in_progress). انتظر ثانية وأعد المحاولة بالمفتاح نفسه: ستحصل على الرسالة الأصلية.
  • بعد الرفض (META_API_ERROR‏، outcome: "refused") لم يُرسَل شيء، لذا فإعادة المحاولة بالمفتاح نفسه تحاول الإرسال من جديد.
  • بعد نتيجة غير معروفة (outcome: "unknown") يعيد المفتاح نفسه دائمًا الرد غير المعروف نفسه ولا يُرسَل مرة أخرى أبدًا — راجع ما يلي.

انتهاء المهلة والنتائج غير المعروفة

إذا انتهت مهلة طلب HTTP الخاص بك، أعد المحاولة بالمفتاح نفسه: إذا كانت Scale قد انتهت، تحصل على ردها؛ وإذا كان الإرسال ما زال جاريًا تحصل على send_in_progress؛ فأعد المحاولة بعد لحظات.

أحيانًا لا تستطيع Scale نفسها أن تعرف: لم يرد WhatsApp في الوقت المحدد، أو رد بخطأ من جهة الخادم، أو رد دون معرّف رسالة. قد تكون الرسالة وصلت إلى العميل وقد لا تكون. عندها ترد Scale بـ 502 META_API_ERROR مع details.outcome: "unknown" و "retry_safe": false و "charged": false. لن تخصمها ولن ترسلها مرة أخرى، لأن WhatsApp لا يوفّر طريقة لـ "الإرسال فقط إن لم تكن أُرسلت من قبل".

لا تُعد إرسال رسالة غير معروفة النتيجة تلقائيًا. راجع المحادثة في Inbox الخاص بـ Scale (أو اسأل العميل) قبل إرسالها مرة أخرى بمفتاح جديد — وإلا قد يستلم العميل الرسالة مرتين.

حالة الرسالة

GET /messages‏/{id}
curl https://getscale.me/wp-json/scale/v1/messages/msg_Q2w3E4r5T6y7U8i9O0p1A2s3 \
  -H "Authorization: Bearer $SCALE_API_KEY"
الرد — 200
{
  "success": true,
  "message": {
    "id": "msg_Q2w3E4r5T6y7U8i9O0p1A2s3",
    "wamid": "wamid.HBgMMjAxMDEyMzQ1Njc4FQIAERgSQzE5",
    "type": "text",
    "to": "201012345678",
    "status": "delivered",
    "failure": null,
    "created_at": "2026-09-24T10:00:00Z"
  }
}
statusالمعنى
pendingسُجّلت الرسالة؛ والاتصال بـ WhatsApp قيد التنفيذ.
sentقبلها WhatsApp (علامة صح واحدة).
deliveredوصلت إلى هاتف العميل.
readفتحها العميل (فقط إذا كان يسمح بإيصالات القراءة).
failedلم تُسلَّم. قيمة failure.stage هي send (رفض WhatsApp الطلب؛ و failure.meta_code هو رمز خطئه؛ ولم يُخصم شيء) أو delivery (قُبلت ثم فشلت؛ و failure.message هو سبب WhatsApp).
unknownلم تستطع Scale معرفة ما إذا كان WhatsApp قد أرسلها — راجع منع التكرار. والإرسال الذي يبقى pending بعد 5 دقائق يُقرأ أيضًا unknown.
فشل في التسليم
{
  "success": true,
  "message": {
    "id": "msg_Q2w3E4r5T6y7U8i9O0p1A2s3",
    "wamid": "wamid.HBgMMjAxMDEyMzQ1Njc4FQIAERgSQzE5",
    "type": "text",
    "to": "201012345678",
    "status": "failed",
    "failure": { "stage": "delivery", "meta_code": null, "message": "Message undeliverable" },
    "created_at": "2026-09-24T10:00:00Z"
  }
}
  • الحالة تتقدّم إلى الأمام فقط (sent‏ ← delivered‏ ← read)؛ والتحديث المتأخر أو المكرر لا يعيدها إلى الخلف أبدًا. قد تصل read دون أن يكون قد أُبلغ عن delivered.
  • إنها الحالة نفسها التي يعرضها Inbox الخاص بـ Scale — تحدّث Scale الاثنين من الـ Webhook الموقّع الخاص بحالات WhatsApp.
  • فضّل الـ Webhooks على الاستعلام الدوري (Polling)؛ وإذا استعلمت دوريًا فافعل ذلك باعتدال — طلبات GET تتشارك حد الـ 300 طلب في الدقيقة.
  • المعرّف التابع لمساحة عمل أخرى، والمعرّف غير المعروف، والمعرّف غير الصالح، كلها ترد بالخطأ نفسه 404 MESSAGE_NOT_FOUND.

حالة اتصال WhatsApp

GET /whatsapp/status
curl https://getscale.me/wp-json/scale/v1/whatsapp/status \
  -H "Authorization: Bearer $SCALE_API_KEY"
الرد — 200
{
  "success": true,
  "connected": true,
  "status": "connected",
  "reason": null,
  "provider": "meta",
  "phone_number": "+201000000001",
  "display_name": "Nile Leather House",
  "waba_id": "123456789012345",
  "phone_number_id": "109876543210987",
  "connected_at": "2026-09-20T08:15:00Z",
  "health": { "checked": true, "checked_at": "2026-09-24T09:58:03Z" },
  "can_send": true,
  "blockers": []
}
الحقلالمعنى
statusconnected‏ · error (مربوط، لكن Meta رفضت الرمز المخزّن — أعد الربط) · connecting (يجري إعداد اتصال) · disconnected (كان مربوطًا ثم فُصل) · pending (لم يُربط أبدًا).
connectedتكون true فقط عندما تكون status هي connected.
reasonسبب عدم صلاحية الاتصال للاستخدام: token_invalid أو provider_unsupported (مربوط عبر Twilio، وهو ما لا تدعمه الواجهة)؛ وإلا null.
providermeta أو twilio أو null عندما لا يوجد رقم مربوط.
phone_number و display_name و waba_id و phone_number_id و connected_atالرقم المربوط، واسم العرض الموثّق، ومعرّفات Meta؛ و null عند عدم الربط.
healthما إذا كانت Scale قد تحققت من الاتصال مع Meta ومتى. نتائج التحقق تُخزَّن مؤقتًا لبضع دقائق؛ والتحقق الذي لم تستطع Meta الرد عليه لا يعني شيئًا في أي اتجاه. عندما ترفض Meta الرمز، يحتوي health.meta_code على رمز خطأ Meta.
can_sendتكون true عندما لا يمنع شيء الإرسال — نافذة الـ 24 ساعة وحدود معدل الطلبات تعتمد على الطلب نفسه ولا تدخل هنا.
blockersما يمنع الإرسال: whatsapp_not_connected أو token_invalid أو provider_unsupported أو subscription_required أو message_limit_reached.

نقطة النهاية هذه للقراءة فقط — لا تغيّر الاتصال ولا لوحة التحكم أبدًا.

Webhooks

يمكن لـ Scale إرسال حدث (POST) إلى نظامك كلما حدث شيء في مساحة العمل. اضبط ذلك من Settings‏ ← Developer & API‏ ← Webhook: رابط https:// الخاص بك (على الإنترنت العام) والأحداث التي تريدها (الأحداث السبعة كلها افتراضيًا). تعرض Scale سرّ التوقيع (whsec_…) مرة واحدة عند إنشاء الـ Webhook وفي كل مرة تقوم فيها بتدويره (Rotate)؛ وبعد ذلك تعرض آخر أربعة أحرف منه فقط. زر "Send test event" يرسل حدث webhook.test لتتحقق من نقطة النهاية الخاصة بك.

كل حدث هو طلب POST بصيغة JSON وبالغلاف نفسه:

الغلاف (Envelope)
{
  "event": "message.delivered",
  "id": "evt_H7j8K9l0Z1x2C3v4B5n6M7q8",
  "created_at": "2026-09-24T10:00:05Z",
  "workspace_id": "ws_90408561-0487-485d-b513-0053582f3d55",
  "data": { }
}

id هو هوية الحدث ولا يتغير أبدًا، حتى عند إعادة إرسال الحدث — أزِل التكرار بناءً عليه. و workspace_id هو المعرّف العام لمساحة العمل. تُرسل الأحداث لمحادثات WhatsApp فقط.

message.received

أرسل عميل رسالة إلى رقم WhatsApp الخاص بمساحة العمل (نص أو صورة أو صوت أو مستند).

data
{
  "message_id": "wamid.HBgMMjAxMDEyMzQ1Njc4FQIAEhgUM0E5",
  "from": "201012345678",
  "type": "text",
  "text": { "body": "Is size 42 available?" },
  "media": null,
  "contact": { "name": "Ahmed" },
  "timestamp": "2026-09-24T10:02:11Z"
}
data — مع وسائط
{
  "message_id": "wamid.HBgMMjAxMDEyMzQ1Njc4FQIAEhgUM0F0",
  "from": "201012345678",
  "type": "image",
  "text": null,
  "media": { "type": "image", "url": "https://getscale.me/wp-content/uploads/scale-ai/img-7f3a.jpg", "mime": "image/jpeg", "caption": "This one" },
  "contact": { "name": "Ahmed" },
  "timestamp": "2026-09-24T10:03:40Z"
}
  • قيمة type هي text أو image أو audio أو document. في الوسائط تكون text بقيمة null ويحتوي media على رابط إلى النسخة التي خزّنتها Scale، ونوعها، ونوع MIME (عند معرفته)، وتعليق العميل. الأنواع الأخرى من الرسائل الواردة لا تحمل media في هذا الإصدار.
  • ما يجب فعله: خزّن الرسالة في سجل العميل (from)، ورد عبر POST /messages — الرد خلال 24 ساعة لا يحتاج إلى قالب.
  • تصل رسالة العميل أيضًا إلى Inbox الخاص بـ Scale، وتُحتسب كرسالة واردة من الرصيد، وقد يرد عليها مساعد الذكاء الاصطناعي الخاص بمساحة العمل إذا كان مفعّلًا.

message.sent

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

data
{
  "message_id": "wamid.HBgMMjAxMDEyMzQ1Njc4FQIAERgSQzE5",
  "api_message_id": "msg_Q2w3E4r5T6y7U8i9O0p1A2s3",
  "to": "201012345678",
  "type": "text",
  "source": "api",
  "status": "sent",
  "text": { "body": "Hello from our store! Your order #123 is confirmed." },
  "template": null,
  "timestamp": "2026-09-24T10:00:01Z"
}
  • قيمة source‏: api (أُرسلت عبر الواجهة)، أو ai (مساعد الذكاء الاصطناعي)، أو scale (شخص في لوحة التحكم، أو حملة).
  • api_message_id هو معرّف msg_… الخاص بك للرسائل المرسلة عبر الواجهة، وإلا null. وقيمة type هي text أو template أو نوع الوسائط؛ وفي حالة القالب يحتوي template على name و language الخاصين به.
  • ما يجب فعله: طابِق الرسائل المرسلة عبر الواجهة مع سجلاتك بواسطة api_message_id؛ واستخدم البقية لعكس المحادثة في نظامك.

message.delivered

data
{
  "message_id": "wamid.HBgMMjAxMDEyMzQ1Njc4FQIAERgSQzE5",
  "api_message_id": "msg_Q2w3E4r5T6y7U8i9O0p1A2s3",
  "to": "201012345678",
  "status": "delivered",
  "timestamp": "2026-09-24T10:00:05Z"
}

وصلت الرسالة إلى هاتف العميل. ما يجب فعله: علّمها كـ delivered.

message.read

data
{
  "message_id": "wamid.HBgMMjAxMDEyMzQ1Njc4FQIAERgSQzE5",
  "api_message_id": "msg_Q2w3E4r5T6y7U8i9O0p1A2s3",
  "to": "201012345678",
  "status": "read",
  "timestamp": "2026-09-24T10:07:30Z"
}

فتحها العميل — فقط إذا كان يسمح بإيصالات القراءة، لذا لا تنتظرها أبدًا.

message.failed

data
{
  "message_id": "wamid.HBgMMjAxMDEyMzQ1Njc4FQIAERgSQzE5",
  "api_message_id": "msg_Q2w3E4r5T6y7U8i9O0p1A2s3",
  "to": "201012345678",
  "status": "failed",
  "timestamp": "2026-09-24T10:00:06Z",
  "error": { "message": "Message undeliverable" }
}

قبل WhatsApp الرسالة ثم لم يستطع تسليمها. error.message هو سبب WhatsApp. ما يجب فعله: أحِلها إلى شخص للمراجعة — لا تُعد إرسالها بشكل أعمى.

أحداث الحالة تتقدّم إلى الأمام فقط: الحالة التي ستعود إلى الخلف (delivered بعد read) أو تتكرر لا تُرسَل، ولا يُرسَل حدث message.sent ثانٍ للرسالة نفسها.

whatsapp.connected

data
{
  "phone_number": "+201000000001",
  "phone_number_id": "109876543210987",
  "waba_id": "123456789012345",
  "timestamp": "2026-09-24T10:04:12Z"
}

تم ربط رقم WhatsApp بمساحة العمل — من لوحة التحكم أو عبر الربط المستضاف. ما يجب فعله: فعّل ميزات WhatsApp في نظامك.

whatsapp.disconnected

data
{
  "phone_number_id": "109876543210987",
  "timestamp": "2026-09-24T12:00:00Z"
}

فُصل الرقم في Scale. عمليات الإرسال ترد بـ 403 WHATSAPP_NOT_CONNECTED حتى يُربط من جديد. ما يجب فعله: أوقف الإرسال مؤقتًا وأبلغ مستخدميك.

webhook.test

data
{ "message": "This is a test event from Scale.", "timestamp": "2026-09-24T09:00:00Z" }

يُرسَل فقط عند الضغط على "Send test event". وهو موقّع مثل أي حدث آخر.

أمان الـ Webhooks

الترويسةالقيمة
X-Scale-Eventاسم الحدث، مثل message.delivered.
X-Scale-Event-Idمعرّف الحدث (evt_…) — هو نفسه id في جسم الطلب.
X-Scale-Timestampوقت Unix بالثواني الذي وُقّعت فيه محاولة التسليم هذه.
X-Scale-Signaturesha256= متبوعًا بقيمة HMAC-SHA256 بالنظام الست عشري (Hex).
X-Scale-Delivery-Attempt1 للمحاولة الأولى، و 2 لأول إعادة محاولة، وهكذا.

التوقيع هو HMAC-SHA256(secret, timestamp + "." + raw body) بترميز Hex ومسبوقًا بـ sha256=. للتحقق من حدث:

  1. اقرأ جسم الطلب الخام بايتًا بايتًا — قبل أي تحليل أو إعادة ترميز لـ JSON.
  2. احسب sha256= + قيمة HMAC-SHA256 بترميز Hex لـ X-Scale-Timestamp + . + الجسم الخام، باستخدام سرّ الـ Webhook كمفتاح.
  3. قارِن النتيجة مع X-Scale-Signature بمقارنة ثابتة الزمن (Constant-time).
  4. ارفض أي طابع زمني يبعد أكثر من 5 دقائق عن ساعة خادمك (للحماية من إعادة الإرسال Replay).
  5. أزِل التكرار بناءً على X-Scale-Event-Id: قد يصل الحدث نفسه أكثر من مرة.
PHP
<?php
$secret = getenv('SCALE_WEBHOOK_SECRET');          // whsec_…
$body   = file_get_contents('php://input');         // the RAW body
$ts     = $_SERVER['HTTP_X_SCALE_TIMESTAMP'] ?? '';
$sig    = $_SERVER['HTTP_X_SCALE_SIGNATURE'] ?? '';

$fresh    = ctype_digit($ts) && abs(time() - (int) $ts) <= 300;
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, $secret);

if (!$fresh || !hash_equals($expected, $sig)) {
    http_response_code(401);
    exit;
}

$event = json_decode($body, true);
// Seen $event['id'] before? Answer 200 and stop.
// Otherwise record it, answer 200 quickly, and do the work afterwards.
http_response_code(200);
Node.js‏ (Express)
const crypto = require('crypto');
const express = require('express');
const app = express();

app.post('/scale/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const secret = process.env.SCALE_WEBHOOK_SECRET;          // whsec_…
  const ts  = req.get('X-Scale-Timestamp') || '';
  const sig = req.get('X-Scale-Signature') || '';
  const raw = req.body.toString('utf8');                    // the RAW body

  const fresh = /^\d+$/.test(ts) && Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(ts + '.' + raw).digest('hex');
  const a = Buffer.from(expected), b = Buffer.from(sig);
  if (!fresh || a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(raw);
  // De-duplicate on event.id, answer quickly, process afterwards.
  res.sendStatus(200);
});
تدوير السرّ يسري فورًا. بعد "Rotate secret" يُوقَّع كل تسليم — بما في ذلك إعادة محاولة الأحداث التي أُنشئت قبل التدوير — بالسرّ الجديد. حدّث خادمك أولًا (أو اقبل السرّين لفترة قصيرة)، ثم نفّذ التدوير.

إعادة المحاولة والتسليم في الـ Webhooks

  • النجاح هو أي رد 2xx خلال 5 ثوانٍ. أجب بسرعة ونفّذ الأعمال البطيئة بعد ذلك.
  • أي شيء آخر — رد 3xx لا ينتهي بـ 2xx، أو أي 4xx أو 5xx، أو انتهاء المهلة، أو خطأ في الاتصال — يُعد محاولة فاشلة، ويُعاد إرسال الحدث بعد 1، 2، 5، 15، 30، 60 و 120 دقيقة.
  • بعد 8 محاولات إجمالًا يُعلَّم الحدث كفاشل نهائيًا ولا يُرسَل مرة أخرى. تعرض بطاقة Webhook في لوحة التحكم عمليات التسليم الأخيرة ونتيجتها.
  • إعادة المحاولة ترسل الحدث نفسه: X-Scale-Event-Id نفسه وجسمًا مطابقًا بايتًا بايتًا. فقط X-Scale-Timestamp و X-Scale-Signature و X-Scale-Delivery-Attempt تكون جديدة — لذا تحقّق من التوقيع في كل محاولة، وأزِل التكرار بناءً على معرّف الحدث.
  • الترتيب: الأحداث الخاصة بالرسالة نفسها تُسلَّم بترتيب حدوثها — message.sent ثم message.delivered ثم message.read؛ والحدث اللاحق ينتظر أثناء إعادة محاولة حدث سابق (والحدث السابق الذي يفشل نهائيًا يُطلق التالي). أما الأحداث الخاصة برسائل مختلفة فلا ترتيب بينها.
  • التسليم غير متزامن (Asynchronous) ومضمون مرة واحدة على الأقل (At-least-once). لا تستدعي Scale نقطة النهاية الخاصة بك من داخل طلب آخر أبدًا، ونقطة نهاية بطيئة أو فاشلة لا تؤخر أبدًا الـ Webhooks الخاصة بأعمال أخرى.
  • تغيير رابط الـ Webhook يسري على الأحداث التي ما زالت تنتظر؛ وحذف الـ Webhook يُنهي التسليم، وتُسقَط الأحداث التي لم تُسلَّم بعد.

الاستخدام والفوترة

ليس للواجهة حصة أو رصيد أو باقة خاصة بها. كل رسالة عبر الواجهة تستخدم رصيد رسائل مساحة العمل نفسه الذي يستخدمه Inbox ومساعد الذكاء الاصطناعي والحملات.

  • تُستخدم رسالة واحدة عندما يقبل WhatsApp رسالة مرسلة عبر الواجهة (أي نوع، بما في ذلك القوالب).
  • لا يُستخدم شيء عندما تُرفض الرسالة قبل الإرسال، أو يرفضها WhatsApp‏ (outcome: "refused")، أو تنتهي بـ unknown.
  • رسائل العملاء الواردة، وردود لوحة التحكم، وردود الذكاء الاصطناعي، ورسائل الحملات (رسالة لكل مستلم) تستخدم الرصيد نفسه.
  • عند نفاد الرصيد ترد عمليات الإرسال بـ 403 MESSAGE_LIMIT_REACHED؛ ودون اشتراك فعّال بـ 403 SUBSCRIPTION_REQUIRED.
  • رسوم محادثات WhatsApp الخاصة بـ Meta منفصلة: تفوترها Meta على حساب WhatsApp Business.
GET /usage
curl https://getscale.me/wp-json/scale/v1/usage \
  -H "Authorization: Bearer $SCALE_API_KEY"
الرد — 200
{
  "success": true,
  "period": { "basis": "subscription_cycle", "starts_at": "2026-09-01T00:00:00Z", "ends_at": "2026-10-01T00:00:00Z" },
  "messages": { "limit": 10000, "used": 812, "remaining": 9188, "unlimited": false },
  "breakdown": { "dashboard": 120, "ai": 390, "api": 214, "incoming": 88, "campaigns": 0, "total": 812 },
  "shared_allowance": true,
  "can_send": true
}
الحقلالمعنى
periodقيمة basis هي subscription_cycle (دورة الفوترة الحالية) أو last_30_days؛ و starts_at / ends_at بتوقيت UTC.
messagesالرصيد الواحد: limit و used و remaining. تكون limit و remaining بقيمة null عندما تكون unlimited بقيمة true.
breakdownمصدر الرسائل المستخدمة في الفترة نفسها: dashboard (أشخاص في Scale)، و ai، و api، و incoming (رسائل العملاء)، و campaigns، ومجموعها total.
shared_allowanceدائمًا true — الواجهة تتشارك رصيد مساحة العمل.
can_sendما إذا كان الاشتراك والرصيد يسمحان بالإرسال الآن.

حدود معدل الطلبات (Rate Limits)

الطلباتالحد
POST /messages60 طلبًا في الدقيقة لكل مساحة عمل
كل طلبات الواجهة الأخرى300 طلب في الدقيقة لكل مساحة عمل
  • الحدود تخص مساحة العمل: كل مفاتيح API الخاصة بها تتشاركها.
  • كل دقيقة نافذة ثابتة على الساعة؛ ويُعاد ضبط الحد مع بداية الدقيقة التالية.
  • كل رد يذكر X-RateLimit-Limit و X-RateLimit-Remaining و X-RateLimit-Reset (وقت Unix الذي تُعاد فيه النافذة).
  • عند تجاوز الحد: 429 RATE_LIMITED مع الترويسة Retry-After بالثواني. الطلب المرفوض لا يفعل شيئًا — لا رسالة ولا خصم.
  • رد 429 مختلف — details.reason: "send_in_progress" و Retry-After: 1 — يعني أن رسالة أخرى من مساحة العمل كانت قيد الإرسال في تلك اللحظة. أعد المحاولة بالقيمة نفسها لـ Idempotency-Key.
429 — طلبات كثيرة جدًا
HTTP/1.1 429 Too Many Requests
Retry-After: 37
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1790161260

{
  "success": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "Too many requests for this workspace. Retry after the number of seconds in Retry-After.",
    "details": { "limit": 60, "retry_after": 37 }
  },
  "request_id": "req_k2V9pQ7sL1mN4xR8tY3wZ6aB"
}

الأخطاء

اقرأ error.code — فهو ثابت. أما error.message فهو موجّه للبشر وقد يتغير؛ و error.details يضيف حقائق عند وجودها.

HTTPerror.codeالمعنىما يجب فعله
401INVALID_API_KEYالمفتاح مفقود أو مشوّه أو غير معروف — أو أُرسل مفتاح في الرابط.أرسل Authorization: Bearer sk_live_… في الترويسة فقط.
401API_KEY_REVOKEDأُلغي المفتاح من لوحة التحكم.استخدم مفتاحًا فعّالًا آخر.
403API_ACCESS_DISABLEDالوصول إلى Public API غير مفعّل لمساحة العمل هذه. لم يُنفَّذ شيء.تواصل مع Scale لتفعيله. المفاتيح والإعدادات محفوظة.
404WORKSPACE_NOT_FOUNDمساحة العمل الخاصة بالمفتاح مؤرشفة أو محذوفة.تواصل مع مالك مساحة العمل.
403WHATSAPP_NOT_CONNECTEDلا يوجد رقم WhatsApp مربوط (details.reason: not_connected)، أو أنه مربوط عبر Twilio‏ (provider_unsupported).اربط WhatsApp (من لوحة التحكم أو عبر الربط المستضاف).
403SUBSCRIPTION_REQUIREDلا يوجد اشتراك فعّال؛ و details.reason مثل not_subscribed أو plan_expired أو suspended.جدّد الاشتراك.
403MESSAGE_LIMIT_REACHEDنفد رصيد رسائل مساحة العمل.اشحن الرصيد أو انتظر الدورة التالية.
400INVALID_MESSAGEجسم الطلب ليس كائن JSON، أو قيمة Idempotency-Key غير صالحة.صحّح الطلب.
422INVALID_MESSAGEحقل مفقود أو غير صالح (النوع، أو النص، أو القالب، أو رابط الوسائط، أو طول التعليق). الرسالة تذكر أيّها.صحّح الطلب.
422INVALID_PHONE_NUMBERقيمة to ليست رقمًا دوليًا صالحًا للاستخدام، أو تخص محادثة غير WhatsApp في مساحة العمل.أرسل الرقم الكامل مع رمز الدولة.
422OUTSIDE_MESSAGING_WINDOWنص أو وسائط خارج نافذة الـ 24 ساعة.أرسل قالبًا معتمدًا.
409IDEMPOTENCY_CONFLICTاستُخدمت قيمة Idempotency-Key هذه لطلب مختلف (details.message_id).استخدم مفتاحًا جديدًا للرسالة الجديدة.
429RATE_LIMITEDبلغت حد معدل الطلبات، أو details.reason: send_in_progress.انتظر Retry-After ثانية، ثم أعد المحاولة بالمفتاح نفسه.
502META_API_ERRORdetails.outcome: "refused" — رفض WhatsApp الرسالة (meta_code و meta_message)؛ لم يُرسَل شيء ولم يُخصم شيء. details.outcome: "unknown" — لم يؤكد WhatsApp؛ لم تُخصم، ولن تُرسَل مرة أخرى أبدًا (retry_safe: false).عند الرفض: صحّح وأعد المحاولة. عند النتيجة غير المعروفة: راجع Inbox قبل الإرسال مرة أخرى بمفتاح جديد.
404MESSAGE_NOT_FOUNDلا توجد رسالة كهذه في مساحة العمل هذه.تحقّق من معرّف msg_….
404ONBOARDING_SESSION_NOT_FOUNDلا توجد جلسة ربط كهذه في مساحة العمل هذه.تحقّق من معرّف onb_….
409WHATSAPP_ALREADY_CONNECTEDيوجد اتصال WhatsApp يعمل بالفعل.افصل الاتصال في Scale قبل ربط رقم آخر.
422INVALID_RETURN_URLقيمة return_url غير مسجّلة بالضبط، أو ليست رابط https:// صالحًا.سجّلها في Developer & API‏ ← Onboarding return URLs.
503ONBOARDING_UNAVAILABLEالربط المستضاف غير متاح الآن.اربط من لوحة التحكم، أو حاول لاحقًا.
500INTERNAL_ERRORحدث خطأ من جهة Scale. لم يُرسَل شيء.أعد المحاولة لاحقًا؛ واذكر request_id إذا استمر الخطأ.
رفض من WhatsApp
{
  "success": false,
  "error": {
    "code": "META_API_ERROR",
    "message": "WhatsApp refused the message. Nothing was sent and nothing was charged.",
    "details": {
      "message_id": "msg_Q2w3E4r5T6y7U8i9O0p1A2s3",
      "outcome": "refused",
      "meta_code": 132001,
      "meta_message": "Template name does not exist in the translation",
      "charged": false
    }
  },
  "request_id": "req_k2V9pQ7sL1mN4xR8tY3wZ6aB"
}
نتيجة غير معروفة
{
  "success": false,
  "error": {
    "code": "META_API_ERROR",
    "message": "WhatsApp did not confirm this message. It may or may not have been delivered, so it will NOT be sent again and was not charged. Check the conversation before sending a new one.",
    "details": {
      "message_id": "msg_Q2w3E4r5T6y7U8i9O0p1A2s3",
      "outcome": "unknown",
      "charged": false,
      "retry_safe": false,
      "replayed": false
    }
  },
  "request_id": "req_k2V9pQ7sL1mN4xR8tY3wZ6aB"
}

قائمة التحقق قبل تشغيل التكامل في Production

  • الوصول إلى Public API مفعّل لمساحة العمل.
  • مفتاح API مُنشأ، ومحفوظ على خادمك فقط، وليس أبدًا في رابط أو متصفح أو تطبيق.
  • WhatsApp مربوط — GET /whatsapp/status تعرض "can_send": true.
  • الـ Webhook مضبوط على رابط https://، وسرّه محفوظ بأمان.
  • كل Webhook يُتحقق منه: التوقيع، وحداثة الطابع الزمني، والمقارنة ثابتة الزمن.
  • أحداث الـ Webhook يُزال تكرارها بناءً على معرّف الحدث، ونقطة النهاية الخاصة بك ترد بـ 2xx خلال 5 ثوانٍ.
  • كل عملية إرسال تحمل Idempotency-Key ذا معنى، ويُعاد استخدامه عند إعادة المحاولة.
  • الرد 429 يُعالَج باستخدام Retry-After؛ و send_in_progress يُعاد بالمفتاح نفسه.
  • الخطأ META_API_ERROR يُعالَج: refused يُصحَّح ويُعاد، و unknown يُحال إلى شخص.
  • النصوص والوسائط تُرسل فقط داخل نافذة الـ 24 ساعة؛ والقوالب تُستخدم لبدء المحادثات.
  • حالة الرسائل تُتابَع عبر الـ Webhooks (أو عبر GET /messages/{id} من حين لآخر).
  • سلوك إعادة المحاولة جُرّب على نقطة نهاية Staging الخاصة بك (الرد بـ 500 مرة واحدة، ثم 200).

مثال تكامل كامل: نظام CRM

نظام CRM يرسل تأكيدًا للطلب ويُبقي المحادثة متزامنة.

  1. ينشئ المالك مفتاح API في Scale ويحفظه في إعدادات الخادم الخاصة بالـ CRM؛ ويحفظ الـ CRM رابط الـ Webhook وسرّه في Scale.
  2. يتحقق الـ CRM عبر GET /whatsapp/status. إذا كانت can_send بقيمة false لأن WhatsApp غير مربوط، يستدعي POST /whatsapp/onboarding/session ويعرض على المالك onboarding_url؛ ويصل whatsapp.connected عند الانتهاء.
  3. يُسجَّل طلب جديد. لم يكتب العميل خلال آخر 24 ساعة، لذا يرسل الـ CRM القالب المعتمد order_confirmation مع Idempotency-Key: order-5001-confirmation، ويحفظ معرّف msg_… المُعاد مع الطلب.
  4. تصل message.sent ثم message.delivered ثم message.read؛ فيطابقها الـ CRM بواسطة api_message_id ويحدّث السجل الزمني للطلب.
  5. يرد العميل — فيصل message.received ومعه from والنص. يربط الـ CRM الرسالة بالعميل.
  6. خلال 24 ساعة من ذلك الرد، يجيب الـ CRM برسالة نصية عادية.
3 — القالب
curl -X POST https://getscale.me/wp-json/scale/v1/messages \
  -H "Authorization: Bearer $SCALE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-5001-confirmation" \
  -d '{
    "to": "201012345678",
    "type": "template",
    "template": {
      "name": "order_confirmation",
      "language": "en",
      "components": [ { "type": "body", "parameters": [ { "type": "text", "text": "#5001" } ] } ]
    }
  }'
5 — وصول الرد
{
  "event": "message.received",
  "id": "evt_P9o8I7u6Y5t4R3e2W1q0A9s8",
  "created_at": "2026-09-24T11:20:00Z",
  "workspace_id": "ws_90408561-0487-485d-b513-0053582f3d55",
  "data": {
    "message_id": "wamid.HBgMMjAxMDEyMzQ1Njc4FQIAEhgUQkE3",
    "from": "201012345678",
    "type": "text",
    "text": { "body": "Thanks! Can you deliver on Saturday?" },
    "media": null,
    "contact": { "name": "Ahmed" },
    "timestamp": "2026-09-24T11:20:00Z"
  }
}
6 — الإجابة داخل النافذة
curl -X POST https://getscale.me/wp-json/scale/v1/messages \
  -H "Authorization: Bearer $SCALE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-5001-reply-1" \
  -d '{"to":"201012345678","type":"text","text":{"body":"Yes — Saturday between 10 am and 2 pm."}}'
ما زلت تحتاج مساعدة؟
فريقنا يرد على أسئلة الإعداد خلال يوم عمل واحد.
تواصل مع الدعم