- أنشئ حساباً ثم مفتاحاً تجريبياً من لوحة التحكم ← مفاتيح API، وصدّره: export ENVOISMS_API_KEY=env_test_…
- الصق أحد الأمثلة المجاورة: يعمل كما هو بهذا المفتاح دون إرسال أو خصم أي شيء.
- سجّل webhook لاستقبال حالات التسليم والردود.
- اشحن رصيدك وأنشئ مفتاحاً حقيقياً (smr_…) واستبدل به المفتاح التجريبي في إعداداتك.
1. إرسال رسالة SMS
طلب واحد فقط. مع مفتاح تجريبي (env_test_…) يكون الإرسال محاكاة: لا يخرج شيء من المنصة ولا يُخصم أي مبلغ، وتُظهر الاستجابة السعر الذي كان سيدفعه مفتاح حقيقي (simulated_cost).
الاستجابةHTTP/1.1 202 Accepted
{
"id": "msg_9f2c4e1a7b3d",
"to": "+212612345678",
"channel": "sms",
"cascade": false,
"status": "queued",
"cost": { "eur": 0, "mad": 0 },
"sandbox": true,
"simulated_cost": { "eur": 0.0704, "mad": 0.792 },
"segments": 1,
"created_at": "2026-10-22T10:30:00.000Z"
}curl https://api.envoisms.ma/v1/messages \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"to": "+212612345678",
"message": "Your order #1042 has shipped."
}'2. إرسال نموذج واتساب
يُرسل واتساب من رقم واتساب للأعمال الخاص بك، المرتبط مرة واحدة في لوحة التحكم (وإلا 403 WHATSAPP_NOT_CONNECTED). خارج نافذة 24 ساعة لا يفتح المحادثة إلا نموذج معتمد؛ وتُمرَّر متغيراته {{1}} و{{2}}… في metadata.variables بالترتيب.
الاستجابةHTTP/1.1 202 Accepted
{
"id": "msg_4b81d0c2e9f7",
"to": "+212612345678",
"channel": "whatsapp",
"cascade": false,
"status": "queued",
"cost": { "eur": 0.05778, "mad": 0.65 },
"segments": 1,
"created_at": "2026-10-22T10:31:00.000Z"
}curl https://api.envoisms.ma/v1/messages \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+212612345678",
"channel": "whatsapp",
"template": { "name": "order_shipped", "language": "fr" },
"metadata": { "variables": ["Karim", "#1042"] }
}'3. التحقق من رقم هاتف (OTP)
تولّد EnvoiSMS الرمز وترسله وتحصي المحاولات وتُنهي صلاحيته (10 دقائق افتراضياً). لا تحفظ سوى session_id. مع مفتاح تجريبي تتضمن الاستجابة sandbox_code لاختبار /verify/check دون هاتف.
الاستجابةPOST /v1/verify/send → 202 Accepted
{
"session_id": "vrf_2c7e9a14b0d3",
"message_id": "msg_e1a07c93b5f2",
"to": "+212612345678",
"channel": "sms",
"expires_at": "2026-10-22T10:42:00.000Z",
"resend_after_seconds": 30,
"fallback_channels": [],
"status": "sent",
"sandbox": true,
"simulated": true,
"sandbox_code": "483921"
}
POST /v1/verify/check → 200 OK
{ "session_id": "vrf_2c7e9a14b0d3", "verified": true, "verified_at": "2026-10-22T10:33:12.000Z" }# 1. Send the code
curl https://api.envoisms.ma/v1/verify/send \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "to": "+212612345678", "brand": "MyApp" }'
# 2. Check what the user typed
curl https://api.envoisms.ma/v1/verify/check \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "session_id": "vrf_…", "code": "483921" }'المصادقة
المصادقة#
يحمل كل طلب /v1 مفتاح API في ترويسة Authorization. أنشئ مفاتيحك وقيّدها وألغها من لوحة التحكم ← مفاتيح API؛ يُعرض المفتاح مرة واحدة عند إنشائه. استخدمه من جهة الخادم فقط.
smr_… يرسل فعلياً ويخصم من الرصيد؛ وenv_test_… يحاكي كل شيء مجاناً. العنوان نفسه للاثنين.
يحمل كل مفتاح قائمة صلاحيات (send وverify وstatus…). وما خرج عنها: 403 FORBIDDEN.
اختيارية لكل مفتاح. يتلقى أي طلب من عنوان IP آخر الرمز 401 UNAUTHORIZED.
X-RateLimit-Limit وX-RateLimit-Remaining وX-RateLimit-Reset (توقيت Unix) في كل استجابة.
POST /v1/messages HTTP/1.1
Host: api.envoisms.ma
Authorization: Bearer smr_your_live_key
Content-Type: application/json
Idempotency-Key: 6f1d2c9e-4b7a-4f3e-9d21-0c5e8a7b3f10
HTTP/1.1 202 Accepted
X-RateLimit-Limit: 180
X-RateLimit-Remaining: 179
X-RateLimit-Reset: 1792663860تأكيد الطلب (SMS)
إرسال تأكيد طلب عبر SMS#
آخر تحديث
- أنشئ مفتاحاً تجريبياً وصدّره: export ENVOISMS_API_KEY=env_test_…
- اختياري: اطلب اسم المرسل الخاص بك (POST /v1/sender-ids، من 15 إلى 30 يوماً). وإلى ذلك الحين احذف sender_id: يُطبق المرسل الافتراضي.
- عند حدث «تم دفع الطلب» أرسل SMS مع Idempotency-Key = معرّف الطلب: الحدث المزدوج يرسل رسالة واحدة.
- سجّل webhook للحدثين message.delivered وmessage.failed؛ ويعود metadata.order_id في كل تسليم.
- جرّب +212600000002 لرؤية مسار الفشل، ثم انتقل إلى مفتاح حقيقي (smr_…).
curl https://api.envoisms.ma/v1/messages \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042-paid" \
-d '{
"to": "+212612345678",
"message": "Commande #1042 confirmée : 349 MAD. Livraison prévue jeudi.",
"metadata": { "order_id": "1042" }
}'طلبات الدفع عند الاستلام عبر واتساب
تأكيد طلبات الدفع عند الاستلام عبر واتساب#
آخر تحديث
على WooCommerce تتولى إضافة EnvoiSMS (v2.0.0) كل شيء: ينتظر كل طلب دفع عند الاستلام، ويتلقى العميل رسالة بزري تأكيد / إلغاء، ونقرته تحدّث الطلب.
- اربط رقم واتساب الخاص بك من لوحة التحكم ← واتساب (يمكنك الاحتفاظ بتطبيق WhatsApp Business على هاتفك).
- ثبّت الإضافة من github.com/envoisms/envoisms-woocommerce (WooCommerce 7.1+، PHP 8.0+) والصق مفتاح API في WooCommerce ← EnvoiSMS Maroc.
- فعّل خيار COD «Activer» واحفظ: تسجل الإضافة الـ webhook الخاص بها (message.inbound) وتحتفظ بسر التوقيع.
- انقر «Créer les modèles WhatsApp» (بالفرنسية والعربية): تراجعها EnvoiSMS ثم ترسلها إلى Meta؛ وتظهر حالتها في الصفحة نفسها.
- بعد اعتماد النماذج، قدّم طلباً تجريبياً بالدفع عند الاستلام. ويمكنك متابعة حالتها عبر الواجهة:
curl "https://api.envoisms.ma/v1/templates?channel=whatsapp" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"حملة تسويقية (API)
إطلاق حملة تسويقية عبر الواجهة#
آخر تحديث
- أنشئ قائمة: POST /v1/contacts/lists {"name": "عملاء أوفياء"} ← lst_…
- استورد جهات الاتصال: POST /v1/contacts/import {"list_id", "contacts": [{"phone", "name"}]}.
- سجّل دليل موافقة كل شخص: POST /v1/consents {"phone", "purpose": "marketing", "status": "granted", "source": "form", "evidence": {…}}.
- أنشئ الحملة: SMS مع body (اذكر طريقة إلغاء الاشتراك)؛ أو واتساب مع template_id وtemplate_variables و"consent_attested": true. ويجدولها scheduled_at.
- أطلقها (POST /v1/campaigns/{id}/send)، وتابعها بـ GET /v1/campaigns/{id}، وأوقفها مؤقتاً بـ /pause. تُستبعد الأرقام الملغى اشتراكها (STOP) تلقائياً.
curl https://api.envoisms.ma/v1/campaigns \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Soldes de novembre",
"channel": "sms",
"list_id": "lst_…",
"body": "-20% ce week-end avec le code NOV20.",
"scheduled_at": "2026-11-07T09:00:00Z"
}'صندوق وارد ثنائي الاتجاه
صندوق دعم ثنائي الاتجاه#
آخر تحديث
- سجّل webhook للحدث message.inbound (وmessage.read إن كنت تعرض إشعارات القراءة).
- تحقق من التوقيع ثم صنّف الرسالة حسب الرقم: data.from هو العميل وdata.channel هي القناة.
- اقرأ سجل العميل عبر GET /v1/conversations/{phone}.
- ردّ عبر POST /v1/conversations/{phone}/messages. على واتساب لا يُسمح بالنص الحر إلا خلال 24 ساعة من آخر رسالة للعميل؛ وبعدها أرسل نموذجاً (template_name) أو انتقل إلى SMS.
- علّم المحادثة كمقروءة عبر POST /v1/conversations/{phone}/mark-read. يصل STOP من العميل كحدث contact.optout ويمنع الإرسال اللاحق.
curl https://api.envoisms.ma/v1/conversations/+212612345678/messages \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "message": "Bonjour, votre colis part aujourd'"'"'hui." }'المنصات والوكالات
ربط واتساب عملائك#
آخر تحديث
لا يُربط رقم واتساب عبر الواجهة: تشترط Meta أن يسجّل صاحب النشاط الدخول بنفسه (Embedded Signup). لذا لكل عميل حساب EnvoiSMS خاص به يدفع ثمن إرسالاته.
- ينشئ العميل حسابه على envoisms.ma (تسجيل بالبريد الإلكتروني).
- يربط رقمه من لوحة التحكم ← واتساب بتسجيل الدخول إلى Meta (Embedded Signup؛ مع إمكانية التعايش مع تطبيق WhatsApp Business).
- ينشئ مفتاح API مقيداً بما تحتاجه (مثل send وstatus) ويسلّمه لك؛ وتحتفظ به في الخادم، مفتاح لكل عميل.
- تحقق من قدرة الرقم على الإرسال: GET /v1/whatsapp/waba-connection ← readiness.can_send وreadiness.blockers.
- أنشئ نماذجه (POST /v1/templates) وأرسل بمفتاحه. تبقى إرسالاته ورصيده وwebhooks الخاصة به ملكاً له.
curl https://api.envoisms.ma/v1/whatsapp/waba-connection \
-H "Authorization: Bearer $CLIENT_API_KEY"البيئة التجريبية والاختبار
اختبر دون إرسال أو دفع#
آخر تحديث
أنشئ مفتاحاً تجريبياً من لوحة التحكم ← مفاتيح API (حدد «Sandbox»). يبدأ بـ env_test_، أما المفتاح الحقيقي فيبدأ بـ smr_. كلاهما يستدعي العنوان نفسه https://api.envoisms.ma/v1: المفتاح هو الذي يحدد وليس العنوان.
يُتحقق من المفتاح التجريبي تماماً مثل المفتاح الحقيقي (صيغة الرقم، معرّف المرسل، نافذة 24 ساعة، المحتوى)، لكن لا يُرسل شيء ولا يُخصم شيء. وما كان المفتاح الحقيقي سيرفضه يُذكر في warnings بدلاً من إخفائه.
- يعيد POST /v1/messages القيمة sandbox: true وتكلفة صفرية وsimulated_cost: السعر الحقيقي للإرسال نفسه.
- يعيد POST /v1/verify/send القيمة sandbox_code: الرمز الذي كان المستخدم سيستلمه.
- لا يرى كل مفتاح إلا نوع حركته: GET /v1/messages بمفتاح تجريبي يعرض الإرسالات المحاكاة فقط، والعكس صحيح.
- أرقام الاختبار (POST /v1/messages، بمفتاح تجريبي فقط): +212600000002 يفشل (message.failed، SANDBOX_TEST_FAILED)؛ +212600000003 غير قابل للتسليم (message.undeliverable)؛ +212600000004 ملغى الاشتراك (403 OPTED_OUT عند الطلب)؛ +212600000005 لا يتلقى إشعاراً أبداً (يبقى «sent»)؛ وأي رقم آخر، بما فيه +212600000001، يُسلَّم (message.delivered) بعد مهلة محاكاة قصيرة (نحو ثانيتين).
- لا يمكن تحويل المفتاح التجريبي إلى حقيقي: أنشئ مفتاحاً حقيقياً عندما تكون جاهزاً وغيّر متغير البيئة.
SMS أم واتساب؟
اختيار القناة المناسبة#
آخر تحديث
SMS هي القناة الافتراضية: احذف channel. تصل إلى أي هاتف دون رقم مرتبط ودون نموذج ودون نافذة 24 ساعة. إنها الخيار الصحيح للإشعارات والتنبيهات لأي عميل.
يتطلب واتساب ("channel": "whatsapp") رقم واتساب للأعمال الخاص بك مرتبطاً. للمبادرة بالكتابة، أو بعد أكثر من 24 ساعة من آخر رسالة للعميل، استخدم نموذجاً معتمداً؛ ولا يُقبل النص الحر إلا خلال 24 ساعة من رسالة العميل (وإلا 400 OUT_OF_24H_WINDOW دون أي خصم). يُسعَّر واتساب حسب فئة النموذج.
الاثنان معاً: "cascade": true يجرب واتساب ثم يتحول إلى SMS إذا رفض واتساب أو فشل أو لم يؤكد التسليم خلال cascade_timeout ثانية (120 افتراضياً). لا يُحتسب إلا ما أُرسل فعلاً.
رموز التحقق: فضّل POST /v1/verify/send الذي يتولى التوليد والمحاولات وانتهاء الصلاحية وإعادة الإرسال، بدلاً من إرسال رمزك بنفسك عبر /v1/messages.
التكرار الآمن وإعادة المحاولة
أعد المحاولة دون الإرسال مرتين#
آخر تحديث
أضف ترويسة Idempotency-Key (حتى 255 حرفاً، مثل UUID أو «order-1042-shipped») إلى POST /v1/messages وPOST /v1/messages/bulk (ومكافئاتها /v1/waba/messages). إذا انتهت مهلة الطلب أو تعطلت عمليتك، أعد الطلب نفسه بالمفتاح نفسه: يتم الإرسال مرة واحدة، وتُعيد المحاولة نص JSON الأصلي مع Idempotency-Replayed: true.
- يُحفظ المفتاح 24 ساعة لكل حساب. تُحفظ كل استجابة أقل من 500 وتُعاد، بما في ذلك أخطاء 4xx (مثل 429 أو 402): بعد خطأ 4xx أصلح السبب واستخدم مفتاحاً جديداً.
- يرتبط المفتاح بالنص نفسه بايتاً ببايت: إعادة المحاولة ترسل النص ذاته. أي نص مختلف بالمفتاح نفسه — حتى JSON نفسه بتنسيق مختلف — يعيد 422 IDEMPOTENCY_KEY_REUSED.
- لا يُحفظ خطأ 5xx أو انتهاء المهلة: أعد المحاولة بالمفتاح نفسه مع انتظار متزايد (1 ث، 2 ث، 4 ث…).
- 409 IDEMPOTENCY_IN_FLIGHT: الطلب الأصلي لا يزال قيد التنفيذ؛ انتظر Retry-After وأعد المحاولة بالمفتاح نفسه. 422 IDEMPOTENCY_KEY_REUSED: استُخدم المفتاح سابقاً مع نص مختلف.
- 429 RATE_LIMITED: انتظر عدد الثواني المذكور في Retry-After. ترافق الترويسات X-RateLimit-Limit وX-RateLimit-Remaining وX-RateLimit-Reset كل استجابة موثّقة.
ترقيم الصفحات
التنقل في القوائم#
آخر تحديث
تقبل القوائم limit وoffset وتعيد { "data": [...], "limit", "offset", "total" }. بالنسبة لـ GET /v1/messages: 50 افتراضياً و200 كحد أقصى، من الأحدث إلى الأقدم، مع التصفية حسب status وchannel وcampaign_id وfrom_date وto_date. أضف format=csv للحصول على كامل النتائج المصفّاة في ملف واحد.
curl "https://api.envoisms.ma/v1/messages?status=failed&limit=100&offset=0" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"صلاحيات المفاتيح
مفتاح واحد بأقل الصلاحيات#
آخر تحديث
يحمل كل مفتاح صلاحيات. أي طلب خارجها يعيد 403 FORBIDDEN. امنح كل خادم أصغر قائمة كافية: خادم رموز التحقق لا يحتاج إلا verify وstatus.
- send — POST /v1/messages و/v1/messages/bulk و/v1/templates و/v1/conversations و/v1/lookup
- status — GET /v1/messages وPOST /v1/verify/check وبقية /v1/verify (sessions وapps وlookup وvoid)
- verify — POST /v1/verify/send و/v1/verify/resend
- balance — GET /v1/billing/balance؛ billing — بقية /v1/billing
- contacts (جهات الاتصال والقوائم والموافقات)، campaigns، webhooks، optouts، analytics، api_keys، sender_id
- إعداد واتساب (/v1/whatsapp/…): التعديلات — ربط رقم، الأدوات، الملف التعريفي، الترحيل — تتطلب جلسة لوحة التحكم (يتلقى مفتاح API الرمز 403). القراءة فقط متاحة للمفاتيح، مثل GET /v1/whatsapp/waba-connection.
الإصدارات والتغييرات
ما الذي قد يتغير، وكيف ستعرف#
آخر تحديث
الإصدار جزء من المسار: /v1. داخل /v1 لا تتغير الواجهة إلا بالإضافة — نقاط نهاية جديدة، حقول اختيارية، حقول في الاستجابة، رموز أخطاء، أحداث webhook. تجاهل الحقول التي لا تعرفها. أي تغيير غير متوافق يصدر في /v2 بجانب /v1 وليس مكانه.
يُعلن عن حذف أي شيء من /v1 قبل 12 شهراً على الأقل: بالبريد لصاحب الحساب، وفي سجل التغييرات، وعلى المسار نفسه الذي تحمل كل استجابة منه حينها Deprecation (تاريخ الإعلان) وSunset (تاريخ التوقف) وLink إلى دليل الترحيل. الاستثناء الوحيد إصلاح أمني يُعلن عنه في أقرب وقت. لا يوجد اليوم أي مسار مُتقادم في /v1.
الحدود لكل نقطة نهاية
الحدود، نقطة نهاية تلو الأخرى#
آخر تحديث
تشترك كل المسارات في حد الدقيقة الخاص بالمفتاح (افتراضياً Starter 60، Business 180، Pro 500، Enterprise 1200؛ وترويسات X-RateLimit-* في كل استجابة؛ و429 RATE_LIMITED مع Retry-After عند التجاوز). وتضيف بعض المسارات قواعدها الخاصة:
- POST /v1/messages/bulk: 10,000 رسالة في كل طلب.
- POST /v1/messages و/bulk: الحصص اليومية والشهرية الاختيارية للحساب (PATCH /v1/billing/quota)؛ وما زاد عنها 429 QUOTA_EXCEEDED حتى منتصف الليل UTC.
- POST /v1/verify/send و/resend: لكل رقم رمز كل 30 ثانية عبر SMS و60 ثانية عبر واتساب (429 OTP_COOLDOWN)، و5 في الساعة و10 في اليوم (429 OTP_RATE_LIMIT)؛ و200 يومياً لكل حساب؛ وخارج المغرب OTP_COUNTRY_PAUSED عندما يُؤكَّد عدد قليل جداً.
- POST /v1/verify/check: 3 محاولات لكل جلسة افتراضياً (قابلة للضبط لكل تطبيق Verify)، ثم 429 MAX_ATTEMPTS.
- POST /v1/inbound-webhooks/catch/{id} (دون مفتاح): 120 طلباً في الدقيقة لكل webhook، و64 كيلوبايت لكل طلب.
- webhooks الخاصة بك: ننتظر ردك 10 ثوانٍ كحد أقصى، ثم نعيد المحاولة 3 مرات (دقيقة، 10 دقائق، ساعة).
Webhooks
استقبال الحالات والردود#
آخر تحديث
أنشئ webhook عبر POST /v1/webhooks ({"url": "https://…", "events": ["message.delivered", "message.failed"]}). تتضمن الاستجابة سر التوقيع ويُعرض مرة واحدة فقط؛ ويصدر POST /v1/webhooks/{id}/rotate-secret سراً جديداً. تقبل events اسماً دقيقاً أو نطاقاً ("message.*") أو "*".
كل تسليم هو طلب POST بصيغة JSON {"event", "data", "timestamp"}. لحالة رسالة تكون data {id, to, channel, status, error_code?, error_message?, metadata?}، وmetadata هي ما أرسلته. أجب بـ 2xx قبل انتهاء المهلة (10 ثوانٍ) ثم عالج بشكل غير متزامن؛ وأزل التكرار بالاعتماد على data.id + event لأن التسليم قد يصل مرتين.
تحقق من X-EnvoiSMS-Signature-256: t=<ثوانٍ>,v1=<hex>: احسب HMAC-SHA256(السر، t + "." + النص الخام)، وقارنه بـ v1 بزمن ثابت، وارفض إذا مضى على t أكثر من 5 دقائق. لا تزال الترويسة القديمة X-EnvoiSMS-Signature (sha256=<hex> للنص وحده دون طابع زمني) تُرسل؛ فضّل الجديدة لأنها تمنع إعادة الإرسال. ويوجد الحدث أيضاً في X-EnvoiSMS-Event.
- إعادة المحاولة: يُعاد إرسال 5xx أو 429 أو خطأ الشبكة 3 مرات (بعد دقيقة و10 دقائق وساعة — 4 محاولات إجمالاً). ولا يُعاد أي 4xx آخر.
- السجل وإعادة الإرسال: يعرض GET /v1/webhooks/logs آخر 100 محاولة؛ ويعيد POST /v1/webhooks/logs/{log_id}/replay إرسال تسليم بتوقيع جديد. ويرسل POST /v1/webhooks/{id}/test مثالاً.
- ترحيل واتساب إلى خادمك (رقم مرتبط مضبوط على وضع «webhook» في لوحة التحكم): تصل الرسائل والحالات بصيغة WhatsApp Cloud الأصلية، موقّعة بـ X-Hub-Signature-256: sha256=<hex> = HMAC-SHA256(webhook_secret الخاص بالرقم، النص الخام). يُعرض هذا السر مرة واحدة عند إنشائه أو تجديده (rotate_webhook_secret). دون سر لا يُرحَّل أي شيء. لا يوجد طابع زمني: أزل التكرار بمعرّف الرسالة.
الأحداث
message.sentسُلّمت الرسالة إلى الشبكة (المشغّل أو واتساب).
message.deliveredأكد هاتف المستلم الاستلام.
message.readواتساب: قرأ المستلم الرسالة (ما لم يُعطّل إشعارات القراءة).
message.failedفشل الإرسال؛ يوضح error_code وerror_message السبب (انظر رموز فشل التسليم).
message.undeliverableأكدت الشبكة تعذّر تسليم الرسالة (رقم غير موجود، أو انتهت صلاحيتها لدى المشغّل).
message.fallbackالتسلسل: تنتقل الرسالة إلى القناة التالية. channel = القناة الجديدة، previous_channel = السابقة، error_code = السبب.
message.inboundراسلك أحد جهات الاتصال (رد SMS أو رسالة واتساب، بما في ذلك الضغط على زر أو قائمة).
message.reactionواتساب: تفاعل أحد جهات الاتصال مع إحدى رسائلك برمز تعبيري.
contact.optout / contact.optinألغى أحد جهات الاتصال اشتراكه (STOP) أو أعاد الاشتراك. يُرفض الإرسال إلى رقم ملغى الاشتراك (403 OPTED_OUT).
lead.qualifiedأنهى بوت التأهيل في واتساب تأهيل عميل محتمل.
import crypto from 'node:crypto';
import express from 'express';
const app = express();
// Keep the RAW body: the signature is computed over the exact bytes received.
app.post('/webhooks/envoisms', express.raw({ type: 'application/json' }), (req, res) => {
const header = req.get('X-EnvoiSMS-Signature-256') || '';
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const expected = crypto
.createHmac('sha256', process.env.ENVOISMS_WEBHOOK_SECRET)
.update(`${parts.t}.${req.body}`)
.digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
const valid = parts.v1?.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
if (!fresh || !valid) return res.sendStatus(401);
const { event, data } = JSON.parse(req.body);
res.sendStatus(200); // answer fast…
queue.push({ event, data }); // …then process (de-duplicate on data.id + event)
});POST /webhooks/envoisms HTTP/1.1
Content-Type: application/json
X-EnvoiSMS-Event: message.delivered
X-EnvoiSMS-Signature-256: t=1792663800,v1=5f0c…e9
X-EnvoiSMS-Signature: sha256=a41b…7d
User-Agent: EnvoiSMS-Webhook/1.0 (formerly SMSMaroc)
{
"event": "message.delivered",
"data": {
"id": "msg_9f2c4e1a7b3d",
"to": "+212612345678",
"channel": "sms",
"status": "delivered",
"metadata": { "order_id": "1042" }
},
"timestamp": "2026-10-22T10:30:04.000Z"
}الأخطاء
الأخطاء#
يعيد الخطأ حالة 4xx أو 5xx ودائماً الغلاف نفسه. اعتمد في منطقك على error.code وليس على النص: error.message جملة بالإنجليزية قد تتحسن صياغتها. كل رمز أدناه يوضح ما يجب فعله.
HTTP/1.1 400 Bad Request
{
"error": {
"code": "INVALID_PHONE",
"message": "to must be E.164 format, for example +212612345678",
"docs": "https://envoisms.ma/en/docs#errors"
}
}المصادقة والوصول
UNAUTHORIZEDHTTP 401ترويسة Authorization مفقودة، أو المفتاح غير معروف أو ملغى، أو عنوان IP غير مسموح به لهذا المفتاح، أو الحساب غير نشط؛ يوضح error.message السبب. أرسل Authorization: Bearer <المفتاح> وتحقق منه في لوحة التحكم ← مفاتيح API.
FORBIDDENHTTP 403المفتاح لا يملك صلاحية هذا المسار (انظر صلاحيات المفاتيح)، أو يتطلب المسار جلسة لوحة التحكم. أضف الصلاحية إلى المفتاح أو استخدم مفتاحاً آخر.
ACCOUNT_SUSPENDEDHTTP 403الحساب معلّق. تواصل مع الدعم؛ لا يمكن إرسال أي شيء حتى تتم مراجعته.
WORKSPACE_NOT_FOUNDWORKSPACE_SUSPENDEDAPI_KEY_WORKSPACE_MISMATCHHTTP 404 / 403مساحة العمل المحددة في ترويسة x-workspace-id غير موجودة أو تم تعليقها أو تتعارض مع المفتاح.
FORBIDDEN_ASSETHTTP 403رقم واتساب هذا لا ينتمي إلى حسابك. استخدم phone_number_id من أرقامك المرتبطة.
PAYMENT_NOT_SETTLEDINVALID_CREDENTIALMACHINE_PAYMENTS_DISABLEDHTTP 402 / 400 / 501مدفوعات الآلات (Authorization: Payment …): لم يُؤكَّد الإيداع بعد (انتظر ثم أعد المحاولة)، أو تعذّر فك ترميز بيانات الاعتماد، أو الميزة معطلة.
طلب غير صالح
INVALID_JSONHTTP 400النص ليس JSON صالحاً. أرسل Content-Type: application/json وكائن JSON.
MISSING_FIELDMISSING_FIELDSINVALID_FIELDMISSING_BODYMISSING_MESSAGEMISSING_URLHTTP 400حقل إلزامي مفقود أو قيمته خارج النطاق؛ يذكر error.message اسم الحقل. صحّحه وأعد الإرسال.
INVALID_PHONEHTTP 400الرقم ليس بصيغة E.164. أرسل + ورمز الدولة ثم الرقم دون مسافات: +212612345678.
INVALID_URLHTTP 400رابط ليس https:// أو غير صالح (webhook أو وسائط أو زر). استخدم رابط https عاماً.
INVALID_CHANNELCHANNEL_NOT_CONFIGUREDCHANNEL_DISABLEDUNSUPPORTED_CHANNELHTTP 400قناة غير معروفة أو غير متاحة. في /v1/messages تكون channel إما sms (الافتراضي) أو whatsapp؛ وفي /v1/consents إما whatsapp أو sms أو any.
INVALID_PURPOSEINVALID_STATUSINVALID_SOURCEINVALID_EVIDENCEINVALID_DATEHTTP 400الموافقات: purpose = marketing | transactional | otp | service؛ status = granted | withdrawn؛ source = api | form | import | dashboard؛ evidence = كائن JSON أقل من 4 كيلوبايت؛ التواريخ بصيغة ISO 8601 وrecorded_at ليس في المستقبل.
INVALID_PERMISSIONSSANDBOX_IMMUTABLEHTTP 400مفاتيح API: امنح صلاحية صالحة واحدة على الأقل؛ ولا يتحول المفتاح أبداً بين التجريبي والحقيقي — أنشئ مفتاحاً جديداً.
NOT_FOUNDLIST_NOT_FOUNDLEAD_NOT_FOUNDTEMPLATE_NOT_FOUNDAPP_NOT_FOUNDSHEET_NOT_FOUNDINVALID_MESSAGE_IDHTTP 400 / 404المورد غير موجود لهذا الحساب (أو لنمط المفتاح هذا: المفتاح التجريبي لا يرى العناصر الحقيقية)، أو أن المعرّف المطلوب غير صالح (مثل null أو غير محدد). تحقق من المعرّف والمفتاح.
METHOD_NOT_ALLOWEDHTTP 405طريقة HTTP هذه غير موجودة على هذا المسار. راجع مرجع نقطة النهاية.
UNSUPPORTED_MEDIA_TYPEPAYLOAD_TOO_LARGEFILE_TOO_LARGEINVALID_FILE_TYPEINVALID_FORM_DATAMISSING_FILEEMPTY_SHEETPARSE_FAILEDMAPPING_ERRORFETCH_FAILEDINVALID_PATHNO_FILE_PROVIDEDNO_DATA_PROVIDEDUPLOAD_FAILEDHTTP 400 / 413 / 415 / 502رُفض الملف أو الاستيراد: نوع غير مدعوم أو حجم كبير أو ملف فارغ أو أعمدة غير معروفة أو رابط يتعذر الوصول إليه. يحدد error.message السبب؛ صحّح الملف وأعد الإرسال.
الإرسال (SMS والعام)
INSUFFICIENT_BALANCEHTTP 402الرصيد غير كافٍ لهذا الإرسال. اشحن من لوحة التحكم ← الفوترة؛ لم يُخصم أي مبلغ.
INTERNATIONAL_REQUIRES_TOPUPHTTP 403الإرسال خارج المغرب متاح للحسابات التي شحنت رصيدها مرة واحدة على الأقل. قم بأول شحن.
QUOTA_EXCEEDEDHTTP 429تم بلوغ الحصة اليومية المحددة لحسابك؛ وتتجدد عند منتصف الليل UTC. ارفعها من لوحة التحكم إن لزم. بعد خطأ 429 هذا أعد المحاولة بمفتاح Idempotency-Key جديد.
OPTED_OUTHTTP 403ألغى هذا المستلم اشتراكه في رسائلك (STOP). لا تُعد الإرسال وأزله من قوائمك. يمكنه إعادة الاشتراك بنفسه.
SENDER_ID_TOO_LONGSENDER_ID_TOO_SHORTSENDER_ID_INVALIDHTTP 400معرّف المرسل الأبجدي الرقمي من 3 إلى 11 حرفاً (حروف وأرقام ومسافة). صحّحه أو احذفه لاستخدام المرسل الافتراضي.
SENDER_ID_GENERICHTTP 400معرّف مرسل عام (INFO، ALERT، SERVICE…) وليس علامة تجارية. استخدم اسم علامتك أو احذفه.
SENDER_ID_PROTECTED_BRANDHTTP 403يشير معرّف المرسل إلى مؤسسة محمية أو يشبهها (بنك، مشغّل، إدارة). مخصّص للحساب المعتمد لهذا الاسم بالضبط.
SENDER_ID_NOT_APPROVEDSENDER_ID_PENDINGSENDER_ID_IN_REVIEWSENDER_ID_REJECTEDSENDER_ID_APPROVEDHTTP 403 / 409تسجيل معرّف المرسل: لم يُعتمد بعد، أو قيد المراجعة، أو مرفوض، أو معتمد مسبقاً (409). تابع حالته في الإعدادات ← معرّفات المرسل.
SENDER_ID_NOT_REGISTEREDHTTP 403لم يعد يُعاد منذ 27/09/2026: يُرسَل معرّف المرسل غير المسجّل تلقائياً بالتسعيرة القياسية. أُبقي عليه كي يظل الكود الذي يتحقق منه صالحاً.
SENDER_NOT_ASSIGNED_TO_WORKSPACEHTTP 403معرّف المرسل المحدد غير مخصص لمساحة العمل هذه. عيّنه في الإعدادات ← مساحات العمل.
NO_SENDER_ID_ASSIGNEDHTTP 422لا يوجد أي معرّف مرسل مخصص لمساحة العمل هذه. خصص معرّف مرسل معتمد في الإعدادات ← مساحات العمل.
WORKSPACE_BALANCE_EXHAUSTEDHTTP 402نفد الرصيد المخصص لمساحة العمل هذه. خصص رصيداً إضافياً من الحساب الرئيسي في الإعدادات ← مساحات العمل.
SPAM_OR_PHISHING_DETECTEDCONTENT_BLOCKEDHTTP 400رُفض المحتوى: رابط تصيّد، أو رسالة تتحدث باسم بنك أو مشغّل أو إدارة، أو مخالفة لشروط الاستخدام. عُلّق الحساب في انتظار المراجعة. للاعتراض: POST /v1/moderation/appeals مع error.appeal.id وerror.appeal.token وملاحظة (دون مفتاح API).
CONTENT_REFUSEDHTTP 400رفض نظام مكافحة الإساءة المحتوى (الرفض الأول). لم يُرسل شيء والحساب لا يزال نشطاً؛ والرفض الثاني يعلّقه. أعد الصياغة أو اعترض عبر POST /v1/moderation/appeals.
INVALID_APPEALAPPEAL_NOT_FOUNDAPPEAL_ALREADY_FILEDHTTP 400 / 404 / 409الاعتراض: المعرّف أو الرمز مفقود أو خاطئ، أو يوجد اعتراض مفتوح بالفعل لهذا الرفض.
EMPTY_BATCHTOO_MANY_MESSAGESHTTP 400يقبل POST /v1/messages/bulk من 1 إلى 10,000 رسالة في كل طلب. قسّم الدفعات الأكبر.
SEND_PERSIST_FAILEDHTTP 502تعذّر تسجيل الرسالة؛ لم يُرسل شيء ولم يُخصم شيء. أعد المحاولة بنفس Idempotency-Key.
واتساب
WHATSAPP_NOT_CONNECTEDHTTP 403لا يوجد رقم واتساب للأعمال مرتبط بالحساب. اربطه من لوحة التحكم؛ وللنص البسيط احذف channel (SMS). لم يُخصم أي مبلغ.
OUT_OF_24H_WINDOWHTTP 400رسالة واتساب حرة إلى جهة لم تراسلك خلال 24 ساعة. أرسل نموذجاً معتمداً (template) أو SMS. لم يُخصم أي مبلغ.
CONTACT_HAS_NO_PHONEHTTP 400تستخدم هذه الجهة اسم مستخدم واتساب ولم تشارك رقمها: لا يمكن الوصول إليها إلا عبر واتساب، وليس عبر SMS. لم يُخصم أي مبلغ.
WHATSAPP_ONLY_FIELDBUTTONS_NOT_SUPPORTEDINTERACTIVE_NOT_SUPPORTEDHTTP 400أُرسل حقل خاص بواتساب (buttons أو interactive أو reaction أو contacts أو location أو sticker أو context) عبر SMS. أضف "channel": "whatsapp" أو احذف الحقل.
CONFLICTING_MESSAGE_TYPESCASCADE_NOT_SUPPORTEDINVALID_MESSAGEHTTP 400تحمل الرسالة نوع محتوى واحداً؛ ولا تُجمع reaction وcontacts وlocation وsticker مع أي شيء وليس لها مقابل SMS (دون cascade).
INVALID_REACTIONINVALID_CONTEXTINVALID_CONTACTSINVALID_LOCATIONINVALID_MEDIAHTTP 400كائن واتساب غير صالح: reaction = {message_id, emoji}؛ context = {message_id}؛ contacts = من 1 إلى 20 بطاقة مع name.formatted_name؛ location = خط عرض وطول رقميان؛ الوسائط = "id" أو "link" بصيغة https، ولا شرح على الصوت/الملصق.
INVALID_INTERACTIVEINVALID_INTERACTIVE_TYPEMISSING_INTERACTIVE_ACTIONINTERACTIVE_BODY_TOO_LONGINVALID_CTA_URL_ACTIONMISSING_CTA_DISPLAY_TEXTCTA_DISPLAY_TEXT_TOO_LONGINVALID_LOCATION_REQUEST_ACTIONINVALID_FLOW_ACTIONMISSING_FLOW_IDMISSING_FLOW_CTAFLOW_CTA_TOO_LONGMISSING_CAROUSEL_CARDSTOO_MANY_CAROUSEL_CARDSINVALID_PRODUCT_ACTIONINVALID_PRODUCT_LISTMISSING_ORDER_ACTIONMISSING_ORDER_CURRENCYMISSING_ORDER_ITEMSMISSING_ORDER_PARAMETERSMISSING_REFERENCE_IDINVALID_TOTAL_AMOUNTMISSING_PHONE_NUMBERHTTP 400رسالة interactive غير صالحة (قائمة، أزرار، cta_url، flow، دوّارة، منتج، طلب، location_request). يذكر error.message الحقل الخاطئ وحد واتساب الذي تجاوزه.
MISSING_BUTTONS_ARRAYINVALID_BUTTONINVALID_BUTTON_COUNTINVALID_BUTTON_TEXTINVALID_BUTTON_TYPEINVALID_REPLY_BUTTONBUTTON_TEXT_TOO_LONGBUTTON_TITLE_TOO_LONGTOO_MANY_BUTTONSMISSING_COPY_CODEMISSING_LIST_BUTTONLIST_BUTTON_TOO_LONGMISSING_LIST_SECTIONSTOO_MANY_SECTIONSSECTION_TITLE_TOO_LONGINVALID_SECTION_ROWSTOO_MANY_ROWSMISSING_ROW_IDMISSING_ROW_TITLEROW_ID_TOO_LONGROW_TITLE_TOO_LONGROW_DESC_TOO_LONGHTTP 400أزرار أو قائمة خارج حدود واتساب: 3 أزرار كحد أقصى، وعنوان الزر 20 حرفاً، و10 أقسام و10 صفوف، وعنوان الصف 24 حرفاً، والوصف 72 حرفاً. يذكر error.message الحد الدقيق.
META_SERVICE_UNAVAILABLEHTTP 502كان واتساب غير متاح مؤقتاً وفشل الإرسال بعد إعادة المحاولات التلقائية. أعد المحاولة بعد بضع دقائق.
النماذج والحملات
TEMPLATE_PENDING_REVIEWTEMPLATE_REJECTED_BY_REVIEWHTTP 409النموذج بانتظار مراجعة EnvoiSMS (يُرسل إلى Meta بعد الموافقة)، أو رُفض في هذه المراجعة: عدّله لإعادته إلى المراجعة.
TEMPLATE_EXISTSTEMPLATE_ALREADY_ON_METATEMPLATE_EDIT_NOT_ALLOWEDTEMPLATE_IMMUTABLE_FIELDHTTP 400 / 409يوجد نموذج بهذا الاسم، أو هو موجود لدى Meta (زامنه)، أو لا يمكن تعديله في حالته الحالية، أو تحاول تغيير حقل تثبّته Meta (الاسم أو اللغة أو فئة نموذج معتمد) — أنشئ نموذجاً جديداً.
INVALID_LANGUAGEINVALID_META_TEMPLATEINVALID_AUTH_TEMPLATEMISSING_SAMPLE_VALUESMISSING_HEADER_EXAMPLEINVALID_HEADER_EXAMPLEHEADER_EXAMPLE_INVALID_SIZEHEADER_EXAMPLE_INVALID_TYPEHEADER_EXAMPLE_UNREACHABLEHTTP 400النموذج لا يحترم قواعد Meta: لغة غير معروفة، أو أمثلة المتغيرات مفقودة، أو وسائط الترويسة مفقودة أو يتعذر الوصول إليها أو من نوع خاطئ أو كبيرة جداً، أو نموذج مصادقة غير مطابق. يوضح error.message ذلك.
INVALID_WABAWHATSAPP_TOKEN_EXPIREDMETA_API_ERRORMETA_SUBMIT_FAILEDMETA_UPLOAD_FAILEDMETA_NOT_CONFIGUREDHTTP 400 / 502 / 503رفضت Meta العملية على حساب واتساب للأعمال الخاص بك: waba_id لا يخصك، أو رمز اتصال منتهي (أعد ربط الحساب من لوحة التحكم)، أو خطأ من Meta. أعد المحاولة؛ وإن استمر تواصل مع الدعم.
TEMPLATE_NOT_APPROVEDMISSING_TEMPLATEMISSING_TEMPLATE_PARAMSHTTP 400حملة أو محادثة واتساب: النموذج غير معتمد أو مفقود أو تنقصه متغيرات. يسرد MISSING_TEMPLATE_PARAMS ما يجب تقديمه في template_variables (_header_media_url أو _button_<i> أو "static:القيمة").
CONSENT_ATTESTATION_REQUIREDHTTP 400تتطلب حملة واتساب التسويقية "consent_attested": true: تؤكد أن كل جهة اتصال في القائمة وافقت على تلقي رسائلك عبر واتساب. أرسلها فقط إن كان ذلك صحيحاً.
INVALID_WABA_SENDERMISSING_LISTEMPTY_LISTLIST_IN_USEHTTP 400الحملة: مرسل واتساب غير مرتبط بحسابك، أو القائمة مفقودة أو بلا مستلمين مؤهلين، أو القائمة مستخدمة في حملة نشطة (ولا يمكن حذفها).
ALREADY_RUNNINGCAMPAIGN_RUNNINGCAMPAIGN_NOT_EDITABLENOT_PAUSEDNOT_RUNNINGHTTP 400 / 409حالة الحملة لا تسمح بهذا الإجراء: لا تُعدَّل إلا الحملة المسودة أو المجدولة، ويجب إيقاف الحملة الجارية مؤقتاً قبل حذفها.
التحقق (OTP)
INVALID_CODEHTTP 400رمز خاطئ. يذكر error.message عدد المحاولات المتبقية؛ دع المستخدم يحاول مجدداً.
MAX_ATTEMPTSHTTP 429محاولات كثيرة: أُغلقت الجلسة. ابدأ جلسة جديدة عبر /v1/verify/send.
SESSION_NOT_FOUNDSESSION_EXPIREDINVALID_STATEINVALID_SESSION_DATAINVALID_SESSION_IDHTTP 400 / 404 / 409جلسة غير معروفة أو منتهية أو معرّف غير صالح (مثل null)، أو تم التحقق منها أو أُلغيت، أو أنشأها مفتاح من النمط الآخر (تجريبي/حقيقي). ابدأ تحققاً جديداً باستخدام المفتاح نفسه لـ send وresend وcheck.
OTP_COOLDOWNOTP_RATE_LIMITHTTP 429أُرسل رمز للتو إلى هذا الرقم (30 ث عبر SMS و60 ث عبر واتساب)، أو أُرسلت رموز كثيرة خلال الساعة. انتظر Retry-After واقترح «إعادة الإرسال» عبر /v1/verify/resend.
OTP_RESEND_LIMIT_EXCEEDEDHTTP 429تم بلوغ الحد الأقصى لمحاولات إعادة الإرسال (4 إرسالات) لهذه الجلسة. انتظر حتى تنتهي صلاحية الجلسة قبل بدء جلسة جديدة.
OTP_COUNTRY_PAUSEDHTTP 429رموز التحقق نحو هذا البلد (خارج المغرب) موقوفة لحسابك حتى 00:00 UTC: عدد قليل جداً من رموز اليوم تم تأكيده، وهو نمط إساءة آلية. يحمل Retry-After الثواني المتبقية.
COUNTRY_NOT_ALLOWEDHTTP 403الرموز نحو هذا البلد غير مفتوحة لحسابك أو لتطبيق Verify الخاص بك. تواصل مع الدعم لفتحها.
WHATSAPP_UNAVAILABLEDELIVERY_FAILEDINVALID_CODE_TYPEBAD_GATEWAYHTTP 400 / 502 / 503تعذّر إرسال الرمز عبر واتساب (غير متاح للحساب، أو مرفوض، أو رمز غير رقمي). لم يُخصم شيء؛ أعد الإرسال مع "channel": "sms".
HLR_UNAVAILABLEHTTP 503تعذّر تنفيذ استعلام HLR (/v1/hlr): الخدمة غير متاحة مؤقتاً أو مقيّدة. أعد المحاولة بعد ثوانٍ؛ لم يُخصم شيء. و502 BAD_GATEWAY على هذا المسار يعني أن الشبكة لم تُجب — القاعدة نفسها، لا خصم.
LOOKUP_FAILEDLOOKUP_UNAVAILABLEHTTP 500 / 503فشل البحث عن المشغّل (/v1/verify/lookup) أو أنه غير متاح. أعد المحاولة لاحقاً؛ لم يُخصم شيء.
حدود الطلبات والتكرار الآمن
RATE_LIMITEDTOO_MANY_REQUESTSRATE_LIMIT_EXCEEDEDHTTP 429تم تجاوز حد الطلبات في الدقيقة لهذا المفتاح. انتظر Retry-After (بالثواني)؛ فالمحاولة قبل ذلك تستهلك النافذة التالية فقط.
INVALID_IDEMPOTENCY_KEYHTTP 400يتجاوز Idempotency-Key 255 حرفاً. استخدم UUID أو معرّفاً قصيراً.
IDEMPOTENCY_IN_FLIGHTHTTP 409الطلب الأصلي الذي يحمل هذا المفتاح لا يزال قيد التنفيذ. انتظر Retry-After وأعد المحاولة بالمفتاح نفسه.
IDEMPOTENCY_KEY_REUSEDHTTP 422استُخدم مفتاح Idempotency-Key هذا مع نص طلب مختلف. المفتاح يخص طلباً واحداً: أنشئ مفتاحاً جديداً.
REPLAY_UNAVAILABLEHTTP 422لا يحتوي تسليم الـ webhook هذا على محتوى محفوظ لإعادة إرساله. لا يمكن إعادة تشغيله.
الحساب والفوترة
LIMIT_EXCEEDEDHTTP 403تم بلوغ الحد الأقصى لمفاتيح API أو الـ webhooks في باقتك. ألغِ أحدها أو غيّر الباقة.
INVALID_AMOUNTINVALID_PACKINVALID_PAYMENT_METHODUNSUPPORTED_METHODALREADY_COMPLETEDTRANSACTION_CANCELLEDRECEIPT_NOT_AVAILABLEINVOICE_NOT_AVAILABLESTRIPE_INVOICES_UNAVAILABLENO_STRIPE_CUSTOMERINVALID_THRESHOLDINVALID_CYCLEINVALID_CYCLE_LIMITNO_PAID_TOPUPNO_FIELDSHTTP 400 / 403 / 404 / 409 / 410الشحن أو الشحن التلقائي: مبلغ أو حد أو سقف أو دورة أو باقة أو وسيلة دفع غير صالحة، أو يلزم شحن يدوي أول، أو عملية ملغاة أو مكتملة مسبقاً، أو لا توجد فاتورة متاحة (بعد).
STRIPE_ERRORPORTAL_ERRORCUSTOMER_CREATION_FAILEDSTRIPE_NOT_CONFIGUREDPAYMENT_METHOD_NOT_CONFIGUREDNOT_AVAILABLEINVOICE_TEMPORARILY_UNAVAILABLEINVOICE_BEING_GENERATEDINVALID_SIGNATUREHTTP 401 / 500 / 502 / 503تعذّر الحصول على صفحة الدفع أو الفاتورة من جانبنا. لم يُخصم أي مبلغ؛ أعد المحاولة بعد لحظات.
من جانب الخادم
INTERNAL_ERRORDB_ERRORSERVICE_UNAVAILABLEBAD_REQUESTHTTP 400 / 500 / 503خطأ من جانبنا. أعد المحاولة مع انتظار متزايد (وبنفس Idempotency-Key للإرسال)؛ وتواصل مع الدعم إن استمر.
رموز فشل التسليم
قد تفشل رسالة مقبولة (202) لاحقاً. يكون error_code الخاص بها — في GET /v1/messages/{id} وفي webhook message.failed — أحد هذه الرموز. تُسترد تلقائياً قيمة رسالة واتساب رُفضت قبل إرسالها.
META_WINDOW_EXPIREDمرّت أكثر من 24 ساعة على آخر رسالة من جهة الاتصال. أرسل نموذجاً معتمداً أو SMS.
META_UNDELIVERABLEلا يمكن الوصول إلى الرقم عبر واتساب. لا إعادة محاولة تلقائية: استخدم SMS.
META_MARKETING_LIMITحد واتساب للرسائل التسويقية لكل مستلم. لا تُعد الإرسال فوراً.
META_RATE_LIMITED / META_PAIR_RATE_LIMITEDتم تجاوز سعة الرقم، أو أُرسلت رسائل كثيرة لهذه الجهة. فشل بعد إعادة المحاولات التلقائية؛ خفّف الوتيرة.
META_POLICY_BLOCKED / META_ACCOUNT_LOCKED / META_PAYMENT_ISSUE / META_PHONE_NOT_REGISTEREDالرقم أو الحساب المرسل في واتساب مقيّد أو مقفل أو بلا وسيلة دفع صالحة لدى Meta أو غير مسجّل. تحقق من WhatsApp Manager؛ رصيدك في EnvoiSMS ليس السبب.
META_TEMPLATE_NOT_FOUND / META_TEMPLATE_PARAM_MISMATCH / META_TEMPLATE_PARAM_FORMATالنموذج غير موجود بهذه اللغة أو لم يُعتمد بعد، أو عدد المتغيرات خاطئ، أو متغير بصيغة خاطئة (وسائط الترويسة أو الزر).
META_UNSUPPORTED_TYPEنوع الرسالة هذا غير مدعوم في واتساب لهذا المستلم.
META_GENERIC_ERROR / META_SERVICE_UNAVAILABLEخطأ مؤقت في واتساب استمر بعد إعادة المحاولات التلقائية. أعد الإرسال بعد بضع دقائق.
CASCADE_TIMEOUTالتسلسل: لم يصل إشعار تسليم خلال cascade_timeout؛ انتقلت الرسالة إلى القناة التالية (حدث message.fallback).
الحدود
الحدود#
لكل مفتاح في الدقيقة. القيمة الافتراضية لمفتاح جديد حسب الباقة: Starter 60، Business 180، Pro 500، Enterprise 1200. وما زاد: 429 RATE_LIMITED مع Retry-After.
حتى 10,000 رسالة في كل طلب POST /v1/messages/bulk.
رمز من 4 إلى 8 أرقام (6 افتراضياً)، صالح من 60 ثانية إلى 30 دقيقة (10 دقائق افتراضياً). إعادة الإرسال بعد 30 ثانية عبر SMS و60 ثانية عبر واتساب.
مفتاح Idempotency-Key حتى 255 حرفاً، يُحفظ 24 ساعة لكل حساب.
npm install envoismspip install envoismscomposer require envoisms/envoisms-phpcomposer require envoisms/laravel-otpgo get github.com/n4ouri/envoisms-gonpx -y @envoisms/mcp-serverإرسال رسالة
يضع رسالة SMS أو رسالة واتساب واحدة في قائمة الانتظار نحو مستلم واحد ويخصم ثمنها. يعيد 202 مع معرّف الرسالة؛ وتصل نتيجة التسليم لاحقاً عبر webhook أو عبر GET /v1/messages/:id.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| to | string | مطلوب | رقم المستلم بصيغة E.164 (+212612345678). |
| message | string | مطلوب | نص الرسالة، 1600 حرف كحد أقصى (يُقتطع ما زاد). الاسم البديل: body. اختياري على واتساب عندما يحمل الطلب قالباً أو وسائط أو تفاعلاً أو جهات اتصال أو موقعاً أو رسالة تفاعلية. |
| sender_id | string | اختياري | SMS: الاسم الذي يظهر لدى المستلم (حروف وأرقام ومسافات، من 3 إلى 11 حرفاً؛ يُرفض الاسم العام مثل INFO أو اسم بنك). إن أُغفل، تُرسل الرسالة باسم مرسلنا المشترك EnvoiSMS بسعر 0.792 درهم. اسمك الخاص يعمل دون أي إجراء بسعر 1.70 درهم ما لم يُعتمد بالسجل التجاري، ثم بالسعر العادي بعد اعتماده. واتساب: الرقم المتصل الذي يرسل. الأسماء البديلة: from وsender. |
| channel | string | اختياري | "sms" (الافتراضي) أو "whatsapp". يرسل "whatsapp" من رقم واتساب للأعمال الخاص بك والمتصل (وإلا 403 WHATSAPP_NOT_CONNECTED). ما لم تكن الرسالة قالباً، لا تُقبل رسالة واتساب إلا إذا راسلك جهة الاتصال خلال آخر 24 ساعة؛ وإلا 400 OUT_OF_24H_WINDOW دون أي خصم. |
| cascade | boolean | string[] | اختياري | true: يحاول واتساب ثم يتحول إلى SMS — فوراً إذا رفض واتساب الرسالة أو أبلغ بفشلها، أو إذا لم يصل إشعار التسليم خلال cascade_timeout. يقبل أيضاً قائمة مرتّبة مثل ["whatsapp", "sms"]. لا يُفوتَر إلا الإرسال الذي خرج فعلاً؛ ورسالة واتساب التي لم يصل إشعارها قد خرجت، فتُفوتَر مع رسالة SMS الاحتياطية وتُسترد إذا أبلغ واتساب لاحقاً بفشلها. |
| cascade_timeout | integer | اختياري | مع cascade: عدد ثواني انتظار إشعار التسليم قبل القناة التالية، من 30 إلى 43200. الافتراضي: 120. اجعل مدة صلاحية (TTL) قالب واتساب لا تتجاوز هذه القيمة، حتى لا يستلم الهاتف العائد للاتصال الرسالتين معاً. |
| template | object | اختياري | (واتساب) قالب معتمد في حسابك: {"name"، "language"، "components"?}. توضع قيم متغيرات النص في metadata.variables. النوع الوحيد المسموح به خارج نافذة الـ 24 ساعة؛ ويُسعَّر حسب فئة القالب. |
| metadata | object | اختياري | أزواج مفتاح-قيمة تُحفظ مع الرسالة وتُعاد في Webhooks (لا في GET /v1/messages). مفاتيح تقرؤها المنصة: variables (قيم القالب)، وmedia_url وmedia_type (ترويسة وسائط القالب)، وpurpose: "otp" (انظر الملاحظات). |
| buttons | array | اختياري | (واتساب) حتى 3 أزرار {"type"، "text"}؛ لا يتجاوز text 25 حرفاً. النوع "copy" يتطلب copy_code، و"url" يتطلب url (http أو https)، و"call" يتطلب phone_number. |
| interactive | object | اختياري | (واتساب) رسالة تفاعلية بصيغة واتساب: قائمة ("list")، أزرار رد ("button"، من 1 إلى 3)، استمارة ("flow")، زر رابط ("cta_url") أو طلب موقع ("location_request_message"). |
| image | video | audio | document | sticker | object | اختياري | (واتساب) وسائط عبر "id" (معرّف وسائط مرفوعة) أو عبر "link" (رابط https)، وليس الاثنين معاً. "caption" للصورة والفيديو والمستند؛ و"filename" للمستند. |
| reaction | object | اختياري | (واتساب) {"message_id": wamid، "emoji": "👍"} — تفاعل مع رسالة من المحادثة؛ الرمز الفارغ يزيل التفاعل. غير مُفوتَر. |
| contacts | array | اختياري | (واتساب) بطاقات جهات اتصال (20 كحد أقصى): name.formatted_name إلزامي؛ وphones وemails وurls وaddresses وorg وbirthday اختيارية. |
| location | object | اختياري | (واتساب) دبوس موقع: {"latitude"، "longitude"، "name"?، "address"?}. |
| context | object | اختياري | (واتساب) {"message_id": wamid} — رد مع اقتباس رسالة سابقة. صالح لجميع الأنواع باستثناء reaction. |
| phone_number_id | string | اختياري | (واتساب) معرّف Meta للرقم المتصل الذي يرسل؛ افتراضياً رقمك الافتراضي. |
- صلاحية مفتاح API المطلوبة: send.
- التكرار الآمن: أرسل ترويسة Idempotency-Key (255 حرفاً كحد أقصى) لإعادة محاولة الطلب دون إرسال مزدوج. خلال 24 ساعة يعيد المفتاح نفسه مع النص نفسه الاستجابة الأصلية (مع الترويسة Idempotency-Replayed: true)؛ ومع نص مختلف 422 IDEMPOTENCY_KEY_REUSED؛ وما دام الطلب الأول قيد التنفيذ 409 IDEMPOTENCY_IN_FLIGHT.
- مفتاح تجريبي (env_test_…): لا يُرسل ولا يُخصم شيء؛ قيمة cost صفر، وتضيف الاستجابة "sandbox": true و"simulated_cost" (الثمن الذي كان سيدفعه مفتاح حقيقي) و"warnings" عند الاقتضاء (ما كان سيُرفض لمفتاح حقيقي، مثل نافذة 24 ساعة مغلقة).
- الرموز لمرة واحدة: يتولى /v1/verify/send الدورة كاملة ويبقى المسار الموصى به. إذا كنت تنشئ رموزك بنفسك وترسلها هنا، أضف metadata: {"purpose": "otp"}: عند فشل تسليم مؤكد من الشبكة نحو رقم مغربي والرمز لم يتجاوز 10 دقائق، تعيد المنصة إرساله مرة واحدة عبر مسار SMS آخر، بنفس معرّف الرسالة ودون تكلفة إضافية.
- أخطاء شائعة: 400 INVALID_PHONE و400 OUT_OF_24H_WINDOW و400 SENDER_ID_GENERIC و402 INSUFFICIENT_BALANCE و403 WHATSAPP_NOT_CONNECTED و403 SENDER_ID_PROTECTED_BRAND. لا يُخصم أي طلب مرفوض.
- التنسيق: فواصل الأسطر (\n) تعمل على جميع القنوات. رسالة SMS نص عادي؛ ويعرض واتساب *العريض* و_المائل_ و~المشطوب~.
curl -X POST "https://api.envoisms.ma/v1/messages" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+212612345678",
"message": "Your order #1042 has shipped."
}'{
"id": "msg_3f9a1c7e5b2d4a6f8e0c1b3d",
"to": "+212612345678",
"channel": "sms",
"cascade": false,
"status": "queued",
"cost": { "eur": 0.072, "mad": 0.792 },
"segments": 1,
"created_at": "2026-10-22T09:14:03.512Z"
}إرسال جماعي
يضع حتى 10,000 رسالة في قائمة الانتظار في طلب واحد، لكل منها مستلمها ونصها. يعيد 202 مع batch_id والرسائل المقبولة والمرفوضة إن وُجدت.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| messages | array | مطلوب | مصفوفة من 1 إلى 10,000 كائن: "to" (E.164) و"message" (أو "body")، واختيارياً "metadata" و"buttons" (واتساب). |
| sender_id | string | اختياري | مرسل موحّد للدفعة كاملة (نفس قواعد POST /v1/messages). الأسماء البديلة: from وsender. |
| channel | string | اختياري | "sms" (الافتراضي) أو "whatsapp". على واتساب لا يحمل الإرسال الجماعي قالباً: يجب أن يكون كل مستلم داخل نافذة الـ 24 ساعة الخاصة به. |
- صلاحية مفتاح API المطلوبة: send.
- تُتحقَّق الدفعة كاملة قبل أي إرسال: يكفي إدخال واحد غير صالح لفشل الطلب بـ 400 INVALID_MESSAGE مع قائمة أول 20 إدخالاً خاطئاً (الفهرس والقاعدة). ويُتحقَّق من الرصيد للدفعة كاملة (402 INSUFFICIENT_BALANCE).
- يسرد "rejected" الإدخالات المرفوضة واحداً واحداً (إلغاء اشتراك، نافذة 24 ساعة مغلقة…) مع رمزها؛ ولا تُخصم. لا يظهر إلا عند وجودها. و"estimated_cost" هو تقدير الدفعة قبل الرفض.
- تُقبل الترويسة Idempotency-Key بنفس قواعد POST /v1/messages.
curl -X POST "https://api.envoisms.ma/v1/messages/bulk" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"to": "+212611111111",
"message": "Your appointment is tomorrow at 10:00."
},
{
"to": "+212622222222",
"message": "Your appointment is tomorrow at 11:30."
}
]
}'{
"batch_id": "batch_9a3c5e7f1b2d4c6e8a0f2b4d",
"total": 2,
"channel": "sms",
"estimated_cost": { "eur": 0.144, "mad": 1.584 },
"messages": [
{ "id": "msg_1a2b3c4d5e6f7a8b9c0d1e2f", "to": "+212611111111", "status": "queued" },
{ "id": "msg_3c4d5e6f7a8b9c0d1e2f3a4b", "to": "+212622222222", "status": "queued" }
],
"rejected": [
{ "to": "+212633333333", "code": "OPTED_OUT", "message": "This number opted out of messages from your account" }
]
}مؤشر الكتابة في واتساب
يضع علامة «مقروءة» على رسالة واتساب مستلمة ويُظهر لمرسلها «يكتب…» أثناء تحضير الرد (حتى ترد أو نحو 25 ثانية). لا يُخزَّن ولا يُفوتَر أي شيء.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| message_id | string | مطلوب | معرّف واتساب (wamid) للرسالة المستلمة التي ترد عليها. |
| typing_indicator | boolean | اختياري | القيمة false ترسل إشعار القراءة فقط. الافتراضي: true. |
| phone_number_id | string | اختياري | الرقم المتصل الذي استلم الرسالة؛ افتراضياً رقمك الافتراضي. |
- صلاحية مفتاح API المطلوبة: send.
- مع مفتاح تجريبي لا يصل شيء إلى واتساب وتكون الاستجابة {"status": "simulated"، "sandbox": true، "message_id"، "typing_indicator"}.
curl -X POST "https://api.envoisms.ma/v1/messages/typing" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message_id": "wamid.HBgMMjEyNjEyMzQ1Njc4FQIAEhg",
"typing_indicator": true
}'{
"status": "ok",
"message_id": "wamid.HBgLMjEyNjEyMzQ1Njc4FQIAEhgg",
"typing_indicator": true
}قائمة الرسائل
يعيد رسائل الحساب من الأحدث إلى الأقدم، مع عوامل تصفية وتقسيم على صفحات. format=csv يصدّر المجموعة المصفاة كاملة.
| المعلمة | النوع | الحالة | الوصف |
|---|---|---|---|
| limit | integer | اختياري | عدد النتائج: 50 افتراضياً و200 كحد أقصى. |
| offset | integer | اختياري | عدد النتائج التي يتم تخطيها (للتقسيم على صفحات). الافتراضي: 0. |
| status | string | اختياري | queued أو sent أو delivered أو read (واتساب) أو failed أو undeliverable أو unconfirmed. |
| channel | string | اختياري | sms أو whatsapp. |
| campaign_id | string | اختياري | رسائل حملة واحدة (cmp_…). |
| from_date | string | اختياري | المُنشأة ابتداءً من هذا التاريخ (UTC). اكتبه بصيغة YYYY-MM-DD: يُقارن مع created_at ذي الصيغة "YYYY-MM-DD HH:MM:SS". |
| to_date | string | اختياري | المُنشأة حتى هذا التاريخ (UTC)، بنفس المقارنة. لتضمين يوم كامل، مرّر اليوم التالي. |
| format | string | اختياري | csv: ينزّل المجموعة المصفاة كاملة (حتى سقف التصدير المذكور في الملف) بدل صفحة واحدة. |
- صلاحية مفتاح API المطلوبة: status.
- لا يرى المفتاح إلا رسائل نمطه: المفتاح التجريبي لا يعرض إلا الإرسالات المحاكاة، والمفتاح الحقيقي لا يعرض إلا الإرسالات الفعلية.
curl -X GET "https://api.envoisms.ma/v1/messages?limit=50" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "msg_3f9a1c7e5b2d4a6f8e0c1b3d",
"campaign_id": null,
"to": "+212612345678",
"channel": "sms",
"body": "Votre commande 4512 est expédiée.",
"sender_id": "MABOUTIQUE",
"unicode": 0,
"segments": 1,
"status": "delivered",
"error_code": null,
"error_message": null,
"cost_eur": 0.072,
"cost_mad": 0.792,
"scheduled_at": null,
"sent_at": "2026-10-22 09:14:05",
"delivered_at": "2026-10-22 09:14:09",
"read_at": null,
"failed_at": null,
"created_at": "2026-10-22 09:14:03",
"operator": "Maroc Telecom",
"sandbox": 0
}
],
"limit": 50,
"offset": 0,
"total": 1
}عرض رسالة
يعيد رسالة واحدة وحالة تسليمها. 404 NOT_FOUND إذا لم يكن المعرّف تابعاً للحساب أو لنمط المفتاح.
- صلاحية مفتاح API المطلوبة: status.
- الحالات: queued وsent وdelivered وread (واتساب) وfailed وundeliverable وunconfirmed. تعني "unconfirmed" أن المشغّل لم يؤكد التسليم ولم ينفه؛ وقد يحل محلها إشعار يصل لاحقاً.
- لا يُعاد هنا حقل metadata المقدَّم عند الإرسال؛ بل يرافق Webhooks.
curl -X GET "https://api.envoisms.ma/v1/messages/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"id": "msg_3f9a1c7e5b2d4a6f8e0c1b3d",
"campaign_id": null,
"to": "+212612345678",
"channel": "sms",
"body": "Votre commande 4512 est expédiée.",
"sender_id": "MABOUTIQUE",
"unicode": 0,
"segments": 1,
"status": "delivered",
"error_code": null,
"error_message": null,
"cost_eur": 0.072,
"cost_mad": 0.792,
"scheduled_at": null,
"sent_at": "2026-10-22 09:14:05",
"delivered_at": "2026-10-22 09:14:09",
"read_at": null,
"failed_at": null,
"created_at": "2026-10-22 09:14:03",
"operator": "Maroc Telecom",
"sandbox": 0
}قائمة القوالب
يعيد القوالب النشطة في الحساب (SMS وواتساب) من الأحدث إلى الأقدم، أياً كانت حالتها.
- صلاحية مفتاح API المطلوبة: send.
- "variables" سلسلة JSON تصف بنية القالب: المتغيرات والقيم التوضيحية والترويسة والتذييل والأزرار والدوّار والمراجعة.
- حالات واتساب: pending_admin (مراجعة EnvoiSMS) وpending_meta وapproved وrejected وpaused وdisabled وin_appeal وflagged وdeleted_on_meta. قالب SMS يكون approved فور إنشائه.
curl -X GET "https://api.envoisms.ma/v1/templates" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "tpl_4c1e9a7b3d5f2e8a6c0b1d3f",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"name": "order_shipped",
"channel": "whatsapp",
"body": "Bonjour {{1}}, votre commande {{2}} est expédiée.",
"variables": "{\"variables\":[\"1\",\"2\"],\"parameter_format\":\"positional\",\"sample_values\":{\"1\":\"Amine\",\"2\":\"4512\"},\"header_type\":\"none\",\"header_text\":null,\"footer\":null,\"buttons\":[]}",
"category": "utility",
"active": 1,
"created_at": "2026-10-20 14:02:11",
"status": "approved",
"rejection_reason": null,
"meta_template_id": "1203948576102938",
"language": "fr",
"quality_score": "GREEN",
"quality_updated_at": "2026-10-21 08:00:00",
"waba_id": null
}
]
}إنشاء قالب
ينشئ قالب SMS (قابلاً للاستخدام فوراً) أو قالب واتساب (تراجعه EnvoiSMS ثم يُقدَّم إلى Meta على حساب واتساب للأعمال الخاص بك). يعيد 201 مع السطر المُنشأ.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| name | string | مطلوب | اسم القالب. يُحوَّل إلى أحرف صغيرة؛ وكل حرف غير a-z و0-9 و_ يصبح _. |
| channel | string | مطلوب | sms أو whatsapp أو telegram أو any. |
| body | string | مطلوب | نص القالب. واتساب: 1024 حرفاً كحد أقصى؛ المتغيرات إما كلها مرقّمة ابتداءً من {{1}} ومتتالية، وإما كلها مسمّاة بأحرف صغيرة ({{first_name}})؛ ولا تكون أبداً في بداية النص أو نهايته ولا متلاصقة. غير مطلوب لقالب واتساب "authentication" الذي تحدد Meta نصه. |
| category | string | اختياري | marketing (الافتراضي) أو utility أو authentication. القيمة otp تعني authentication، وtransactional تعني utility. قد تعيد Meta تحديد الفئة عند المراجعة. |
| language | string | اختياري | (واتساب) رمز لغة Meta، مثل fr وar وen_US وes. الافتراضي: fr. |
| sample_values | object | string[] | اختياري | (واتساب) قيمة توضيحية لكل متغير، تشترطها Meta: {"1": "Amine"} أو ["Amine"]. بدونها 400 MISSING_SAMPLE_VALUES. |
| variables | string[] | اختياري | (SMS) أسماء متغيرات النص، تُحفظ كما هي. |
| header_type | string | اختياري | (واتساب) none (الافتراضي) أو text أو image أو video أو document. ترويسة text تأخذ header_text (60 حرفاً، ومتغير واحد كحد أقصى مع header_sample)؛ وترويسة الوسائط تتطلب header_example_url. |
| header_text | header_sample | header_example_url | string | اختياري | (واتساب) نص الترويسة؛ القيمة التوضيحية لمتغيرها؛ رابط https عام لملف توضيحي لترويسة صورة أو فيديو أو مستند (400 MISSING_HEADER_EXAMPLE إذا غاب). |
| footer | string | اختياري | (واتساب) تذييل، 60 حرفاً كحد أقصى. |
| buttons | array | اختياري | (واتساب) حتى 10 أزرار {"type"، "text"} (25 حرفاً). الأنواع: quick_reply؛ url (مع url http(s)، وurl_example إذا انتهى الرابط بمتغير)؛ phone_number (مع phone_number)؛ copy_code (مع copy_code)؛ flow (مع flow_id، معرّف أحد WhatsApp Flows المنشورة لديك، وnavigate_screen، و"LEAD" افتراضياً). |
| carousel | object | اختياري | (واتساب، marketing أو utility) {"header_type": "image" | "video"، "cards": [...]}: من 2 إلى 10 بطاقات، كل منها {"header_example_url" (https)، "body" (160 حرفاً بلا متغير)، "buttons" (1 أو 2، quick_reply أو url بلا متغير)}. تحمل كل البطاقات الأزرار نفسها. ولا يملك قالب الدوّار ترويسة ولا تذييلاً ولا أزراراً خاصة به. |
| add_security_recommendation | code_expiration_minutes | otp_type | supported_apps | mixed | اختياري | (واتساب، authentication) تنبيه الأمان من Meta (قيمة منطقية)؛ صلاحية الرمز من 1 إلى 90 دقيقة؛ COPY_CODE (الافتراضي) أو ONE_TAP أو ZERO_TAP؛ supported_apps [{"package_name"، "signature_hash" (11 حرفاً)}] مطلوبة لـ ONE_TAP وZERO_TAP. ويتطلب ZERO_TAP أيضاً zero_tap_terms_accepted: true. |
| waba_id | string | اختياري | (واتساب) حساب واتساب للأعمال المستهدف إذا كان للحساب أكثر من واحد؛ يجب أن يكون أحد حساباتك المتصلة (وإلا 400 INVALID_WABA). |
- صلاحية مفتاح API المطلوبة: send.
- المراجعة: يُنشأ قالب واتساب بحالة pending_admin. تجري فوراً فحوص آلية (انتحال صفة بنك، رابط تصيّد، مستضيف روابط مؤقتة، حساب غير نشط)؛ فإن لم تجد شيئاً يُقدَّم القالب إلى Meta (pending_meta) وتُظهر الاستجابة ذلك مباشرة. وإلا يبقى pending_admin لمراجعة بشرية. Meta وحدها تعتمده (approved) وتحدد فئته النهائية.
- الأخطاء: 400 MISSING_FIELD وINVALID_CHANNEL وINVALID_LANGUAGE وINVALID_META_TEMPLATE وMISSING_SAMPLE_VALUES وMISSING_HEADER_EXAMPLE وINVALID_AUTH_TEMPLATE وINVALID_WABA؛ و409 TEMPLATE_EXISTS (الاسم واللغة نفسهما نشطان مسبقاً؛ ويُستبدل القالب المرفوض الذي يحمل الاسم نفسه).
curl -X POST "https://api.envoisms.ma/v1/templates" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "order_shipped",
"channel": "whatsapp",
"category": "utility",
"language": "fr",
"body": "Bonjour {{1}}, votre commande {{2}} est expédiée.",
"sample_values": [
"Karim",
"#1042"
]
}'{
"id": "tpl_8e2a4c6b1d3f5a7c9e0b2d4f",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"name": "catalogue_automne",
"channel": "whatsapp",
"body": "Découvrez notre sélection d’automne, {{1}} !",
"variables": "{\"header_type\":\"none\",\"header_text\":null,\"footer\":null,\"buttons\":[],\"carousel\":{\"header_type\":\"image\",\"cards\":[{\"header_example_url\":\"https://example.ma/veste.jpg\",\"body\":\"Veste en laine\",\"buttons\":[{\"type\":\"url\",\"text\":\"Voir\",\"url\":\"https://example.ma/veste\"}]},{\"header_example_url\":\"https://example.ma/pull.jpg\",\"body\":\"Pull en coton\",\"buttons\":[{\"type\":\"url\",\"text\":\"Voir\",\"url\":\"https://example.ma/pull\"}]}]},\"variables\":[\"1\"],\"parameter_format\":\"positional\",\"sample_values\":{\"1\":\"Amine\"}}",
"category": "marketing",
"active": 1,
"created_at": "2026-10-22 09:20:41",
"status": "pending_meta",
"rejection_reason": null,
"meta_template_id": "1203948576102944",
"language": "fr",
"quality_score": null,
"quality_updated_at": null,
"waba_id": null
}عرض قالب
يعيد قالباً نشطاً في الحساب مع حالته و، إن رُفض، rejection_reason. وإلا 404 NOT_FOUND.
- صلاحية مفتاح API المطلوبة: send.
curl -X GET "https://api.envoisms.ma/v1/templates/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"id": "tpl_4c1e9a7b3d5f2e8a6c0b1d3f",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"name": "order_shipped",
"channel": "whatsapp",
"body": "Bonjour {{1}}, votre commande {{2}} est expédiée.",
"variables": "{\"variables\":[\"1\",\"2\"],\"parameter_format\":\"positional\",\"sample_values\":{\"1\":\"Amine\",\"2\":\"4512\"},\"header_type\":\"none\",\"header_text\":null,\"footer\":null,\"buttons\":[]}",
"category": "utility",
"active": 1,
"created_at": "2026-10-20 14:02:11",
"status": "approved",
"rejection_reason": null,
"meta_template_id": "1203948576102938",
"language": "fr",
"quality_score": "GREEN",
"quality_updated_at": "2026-10-21 08:00:00",
"waba_id": null
}تعديل قالب
يعدّل قالباً بنفس حقول الإنشاء؛ لا يتغير إلا ما ترسله. يعيد السطر المحدَّث.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| name | body | category | language | sample_values | header_* | footer | buttons | carousel | mixed | اختياري | نفس الحقول والقواعد في POST /v1/templates. قالب SMS لا يقبل إلا name وbody وcategory وvariables. |
- صلاحية مفتاح API المطلوبة: send.
- واتساب، قالب لم يصل Meta بعد: يعيده التعديل إلى المراجعة (pending_admin ثم الفحوص الآلية). قالب موجود لدى Meta: يُقدَّم التعديل إلى Meta ويعود القالب إلى pending_meta؛ ولا يتغير الاسم ولا اللغة بعد ذلك (400 TEMPLATE_IMMUTABLE_FIELD)، ولا فئة القالب المعتمد؛ ولا تعديل أثناء pending_meta أو in_appeal أو disabled (409 TEMPLATE_EDIT_NOT_ALLOWED).
curl -X PATCH "https://api.envoisms.ma/v1/templates/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"id": "tpl_4c1e9a7b3d5f2e8a6c0b1d3f",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"name": "order_shipped",
"channel": "whatsapp",
"body": "Bonjour {{1}}, votre commande {{2}} est en route.",
"variables": "{\"variables\":[\"1\",\"2\"],\"parameter_format\":\"positional\",\"sample_values\":{\"1\":\"Amine\",\"2\":\"4512\"},\"header_type\":\"none\",\"header_text\":null,\"footer\":null,\"buttons\":[]}",
"category": "utility",
"active": 1,
"created_at": "2026-10-20 14:02:11",
"status": "pending_meta",
"rejection_reason": null,
"meta_template_id": "1203948576102938",
"language": "fr",
"quality_score": "GREEN",
"quality_updated_at": "2026-10-21 08:00:00",
"waba_id": null
}حذف قالب
يعطّل القالب. ويُحذف قالب واتساب الموجود لدى Meta هناك أيضاً؛ يبيّن meta_deleted ما إذا فعلت Meta ذلك، وmeta_note سبب عدمه.
- صلاحية مفتاح API المطلوبة: send.
curl -X DELETE "https://api.envoisms.ma/v1/templates/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"deleted": true,
"id": "tpl_4c1e9a7b3d5f2e8a6c0b1d3f",
"meta_deleted": true
}تقديم قالب إلى Meta
يقدّم إلى Meta قالب واتساب اجتاز مراجعة EnvoiSMS لكنه غير موجود لدى Meta (حذفته Meta مثلاً). ليس وسيلة لتجاوز المراجعة.
- صلاحية مفتاح API المطلوبة: send.
- حالات الرفض: 409 TEMPLATE_PENDING_REVIEW (القالب ينتظر مراجعة EnvoiSMS؛ ويُقدَّم إلى Meta بعد اعتماده)، 409 TEMPLATE_REJECTED_BY_REVIEW (رفضته المراجعة؛ عدّله لإعادته إلى المراجعة)، 409 TEMPLATE_ALREADY_ON_META، و400 INVALID_CHANNEL (ليس قالب واتساب).
curl -X POST "https://api.envoisms.ma/v1/templates/{id}/submit-meta" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"ok": true,
"template": {
"id": "tpl_4c1e9a7b3d5f2e8a6c0b1d3f",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"name": "order_shipped",
"channel": "whatsapp",
"body": "Bonjour {{1}}, votre commande {{2}} est expédiée.",
"variables": "{\"variables\":[\"1\",\"2\"],\"parameter_format\":\"positional\",\"sample_values\":{\"1\":\"Amine\",\"2\":\"4512\"},\"header_type\":\"none\",\"header_text\":null,\"footer\":null,\"buttons\":[]}",
"category": "utility",
"active": 1,
"created_at": "2026-10-20 14:02:11",
"status": "pending_meta",
"rejection_reason": null,
"meta_template_id": "1203948576102951",
"language": "fr",
"quality_score": null,
"quality_updated_at": null,
"waba_id": null
},
"meta": { "id": "1203948576102951", "status": "PENDING", "category": "UTILITY" }
}مزامنة القوالب من Meta
يستورد قوالب حسابات واتساب للأعمال الخاصة بك (الحالة، الفئة، الجودة). يصبح القالب الذي لم يعد لدى Meta بحالة deleted_on_meta، إلا إذا كانت المزامنة ناقصة (complete: false).
- صلاحية مفتاح API المطلوبة: send.
curl -X POST "https://api.envoisms.ma/v1/templates/sync" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"ok": true,
"wabas": ["104857392017465"],
"fetched": 12,
"updated": 9,
"inserted": 3,
"deduped": 0,
"marked_deleted": 1,
"complete": true,
"errors": []
}قائمة مُعرّفات المرسل
يعيد طلبات مُعرّف مرسل SMS الخاصة بك وحالتها، مع اسم مقترح انطلاقاً من ملفك.
- صلاحية مفتاح API المطلوبة: sender_id.
curl -X GET "https://api.envoisms.ma/v1/sender-ids" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "sid_6a2c8e4b0d1f3a5c7e9b2d4f",
"sender_id": "MABOUTIQUE",
"status": "approved",
"rc_url": "/v1/sender-ids/rc/acct_7b2e4c6a8d0f1e3b5c7a9d2f/rc_1f3a5c7e9b2d4f6a8c0e2b4d.pdf",
"note": null,
"requested_at": "2026-10-02 10:15:00",
"reviewed_at": "2026-10-09 16:40:00",
"company_website": "https://maboutique.ma",
"sample_content": "Votre commande 4512 est expédiée.",
"message_type": "transactional",
"estimated_volume": "5000/mois",
"operator_emailed_at": "2026-10-02 10:20:00"
}
],
"suggestedSenderId": "MABOUTIQUE"
}طلب مُعرّف مرسل
يقدّم طلب مُعرّف مرسل SMS مرفقاً بالسجل التجاري بصيغة PDF. النص بصيغة multipart/form-data. يعيد 201 (أو 200 عند تحديث طلب قائم) بحالة pending.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| sender_id | string | مطلوب | الاسم المطلوب: حروف بلا تشكيل وأرقام ومسافات، 11 حرفاً كحد أقصى، وحرف واحد على الأقل. يُرفض الاسم العام (INFO وALERT…) أو اسم مؤسسة محمية. |
| file | file | مطلوب | السجل التجاري، ملف PDF لا يتجاوز 5 ميغابايت. |
| message_type | string | اختياري | transactional (الافتراضي) أو promotional. |
| company_website | sample_content | estimated_volume | string | اختياري | موقع الشركة ومثال للرسالة والحجم المقدَّر، تُرسَل إلى المشغّلين مع الطلب. |
- صلاحية مفتاح API المطلوبة: sender_id.
- الأخطاء: 400 INVALID_FORM_DATA وMISSING_FIELD وINVALID_FIELD وSENDER_ID_GENERIC وINVALID_FILE_TYPE وFILE_TOO_LARGE؛ و403 SENDER_ID_PROTECTED_BRAND؛ و409 SENDER_ID_PENDING أو SENDER_ID_APPROVED.
curl -X POST "https://api.envoisms.ma/v1/sender-ids" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"id": "sid_6a2c8e4b0d1f3a5c7e9b2d4f",
"sender_id": "MABOUTIQUE",
"status": "pending",
"rc_url": "/v1/sender-ids/rc/acct_7b2e4c6a8d0f1e3b5c7a9d2f/rc_1f3a5c7e9b2d4f6a8c0e2b4d.pdf"
}ربط واتساب للأعمال
يعيد حالة أرقام واتساب للأعمال المتصلة لديك وقائمة تحقق الرقم الافتراضي (readiness): ما يمنعه من الإرسال حسب Meta. قراءة فقط؛ ولا يُعاد أي سر.
- أي مفتاح API صالح: لا تُشترط صلاحية بعينها.
- readiness: قيمة can_send هي AVAILABLE أو LIMITED أو BLOCKED أو unknown (لم تُجب Meta)؛ وblockers تسرد أسباب Meta (entity وreason وresolution)؛ وpayment_method تكون missing عندما تشير Meta إلى غياب وسيلة الدفع، وunchecked في غير ذلك؛ وbusiness_verification هي حالة توثيق النشاط لدى Meta. وتكون readiness null في غياب رقم متصل. وما لم تقله Meta يُعاد null، لا كقيمة سليمة افتراضية.
- ربط رقم أو فصله أو إعادة ضبطه، وكل كتابة تحت /v1/whatsapp (الملف التعريفي، الإعدادات، الأدوات)، تتم من جلسة لوحة التحكم: يتلقى مفتاح API 403 FORBIDDEN. ولا يبقى مفتوحاً للمفاتيح إلا POST /v1/whatsapp/lookup-phone وPOST /v1/whatsapp/media/upload.
curl -X GET "https://api.envoisms.ma/v1/whatsapp/waba-connection" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"readiness": {
"waba_id": "104857392017465",
"payment_method": "unchecked",
"can_send": "AVAILABLE",
"blockers": [],
"business_verification": "verified",
"approved_templates": 4,
"checked_at": "2026-10-22T08:00:12.000Z"
},
"connected": true,
"mode": "custom_waba",
"gateway_number": "+212 708-789095",
"waba_id": "104857392017465",
"phone_number_id": "109283746501928",
"phone_number": "+212522000000",
"name": "Ma Boutique",
"status": "connected",
"error": null,
"quality_rating": "GREEN",
"messaging_limit_tier": "TIER_1K",
"connected_at": "2026-09-30 11:02:44",
"meta_app_id": "1029384756102938",
"meta_config_id": "2039485761029384",
"preverified_phone_ids": [],
"connections": [
{
"id": "5f1c9e3a7b2d4f6a8c0e1b3d5f7a9c2e",
"waba_id": "104857392017465",
"phone_number_id": "109283746501928",
"phone_number": "+212522000000",
"verified_name": "Ma Boutique",
"status": "connected",
"is_default": true,
"webhook_url": null,
"settings": {},
"created_at": "2026-09-30 11:02:44",
"updated_at": "2026-10-20 08:15:02"
}
]
}الملف التعريفي لواتساب للأعمال
يعيد الملف التعريفي العام لأحد أرقامك كما تحتفظ به Meta: النبذة، العنوان، الوصف، البريد الإلكتروني، الصورة، المواقع، القطاع.
| المعلمة | النوع | الحالة | الوصف |
|---|---|---|---|
| phone_number_id | string | اختياري | الرقم المتصل المطلوب؛ افتراضياً رقمك الافتراضي. |
- أي مفتاح API صالح: لا تُشترط صلاحية بعينها.
- يُحدَّث الملف التعريفي من لوحة التحكم؛ ومع مفتاح API يعيد POST /v1/whatsapp/profile 403 FORBIDDEN.
curl -X GET "https://api.envoisms.ma/v1/whatsapp/profile" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"ok": true,
"phone_number_id": "109283746501928",
"profile": {
"about": "Service client",
"address": "Casablanca, Maroc",
"description": "Prêt-à-porter, livraison partout au Maroc.",
"email": "contact@maboutique.ma",
"profile_picture_url": "https://pps.whatsapp.net/v/t61/...",
"websites": ["https://maboutique.ma"],
"vertical": "APPAREL"
}
}إرسال رمز تحقق
يولّد رمزاً لمرة واحدة ويرسله ويفتح جلسة تحقق. لا تخزّن أي رمز: تحقق مما يكتبه المستخدم عبر POST /v1/verify/check. يعيد 202.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| to | string | مطلوب | المستلم بصيغة E.164. |
| channel | string | اختياري | "sms" (الافتراضي) أو "whatsapp". على واتساب يُرسل الرمز كقالب مصادقة — من رقمك الخاص إذا ربطت رقماً له قالب مصادقة معتمد، وإلا من رقم EnvoiSMS — ويتحول إلى SMS إذا رفضت Meta الإرسال. |
| app_id | string | اختياري | تطبيق Verify (vra_…) المُنشأ في لوحة التحكم: يحدد الطول والصلاحية وعدد المحاولات والقنوات والتسلسل، ويحل محل code_length وexpiry. |
| code_length | integer | اختياري | طول الرمز من 4 إلى 8 أرقام. الافتراضي: 6. |
| expiry | integer | اختياري | صلاحية الرمز بالثواني من 60 إلى 1800. الافتراضي: 600. وعلى واتساب لا تتجاوز 600. |
| brand | string | اختياري | (SMS) الاسم المعروض في الرسالة، 32 حرفاً كحد أقصى. الافتراضي: EnvoiSMS. يُستبدل الاسم العام بـ ENVOISMS، ويُرفض اسم المؤسسة المحمية (403 SENDER_ID_PROTECTED_BRAND). |
| template | string | اختياري | (SMS) نص مخصص يتضمن {{code}} و{{brand}}. |
| otp_button_text | string | اختياري | (واتساب) تسمية زر النسخ، 25 حرفاً كحد أقصى. |
| otp_type | package_name | signature_hash | string | اختياري | (واتساب) copy_code (الافتراضي) أو one_tap أو zero_tap أو url؛ ومع one_tap وzero_tap اسم حزمة Android وبصمة توقيع التطبيق الذي يستقبل الرمز. |
| web_otp_domain | app_hash | string | اختياري | (SMS) نطاق الملء التلقائي WebOTP (يضيف «@domain #code» إلى الرسالة)؛ وبصمة تطبيق Android ذات 11 حرفاً لواجهة SMS Retriever. |
| auto_cascade | boolean | اختياري | يرسل أيضاً رسالة SMS الاحتياطية عند انتهاء مهلة القناة الأولى دون تسليم. يحل محل إعداد تطبيق Verify. |
- صلاحية مفتاح API المطلوبة: verify.
- على واتساب تحمل الاستجابة "channel": "whatsapp" و"resend_after_seconds": 60 و"fallback_channels": ["sms"، "whatsapp"] و"cost" ({eur، mad})، بلا message_id. ومع مفتاح تجريبي لا يُرسل ولا يُخصم شيء وتضيف الاستجابة "sandbox": true و"simulated": true و"sandbox_code"، أي الرمز الذي يُقدَّم إلى /v1/verify/check.
- حدود لكل رقم: إرسال واحد كل 30 ثانية عبر SMS و60 ثانية عبر واتساب (429 OTP_COOLDOWN مع Retry-After)، و5 في الساعة و10 في اليوم (429 OTP_RATE_LIMIT)؛ و200 إرسال في اليوم لكل حساب.
- الملء التلقائي: في موقعك أضف autocomplete="one-time-code" وinputmode="numeric" إلى حقل إدخال الرمز.
curl -X POST "https://api.envoisms.ma/v1/verify/send" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+212612345678",
"brand": "MyApp"
}'{
"session_id": "vrf_7e2a9c4b1d3f5a7c9e0b2d4f",
"message_id": "msg_2b4d6f8a0c1e3b5d7f9a1c3e",
"to": "+212612345678",
"channel": "sms",
"expires_at": "2026-10-22T09:24:03.000Z",
"resend_after_seconds": 30,
"fallback_channels": [],
"status": "sent"
}إعادة إرسال الرمز
يعيد إرسال رمز جلسة لا تزال مفتوحة، عبر SMS أو واتساب، بعد انقضاء فترة الانتظار. لا يتغير الرمز. يعيد 200.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| session_id | string | مطلوب | الجلسة التي أعادها /v1/verify/send (vrf_…). |
| channel | string | اختياري | "sms" (الافتراضي) أو "whatsapp". |
- صلاحية مفتاح API المطلوبة: verify.
- تبدأ فترة الانتظار من آخر إرسال فعلي: 30 ثانية بعد SMS و60 ثانية بعد واتساب. يعطي GET /v1/verify/:id العد التنازلي (resend_available_in_seconds) ويحمل 429 OTP_COOLDOWN الترويسة Retry-After. والنافذة خاصة بحسابك وبالرقم.
- الأخطاء: 404 SESSION_NOT_FOUND (غير معروفة أو منتهية أو من نمط مفتاح آخر)، 400 SESSION_EXPIRED، 400 INVALID_STATE (جلسة سبق التحقق منها أو مغلقة)، 400 UNSUPPORTED_CHANNEL، 429 OTP_COOLDOWN. ومع مفتاح تجريبي تضيف الاستجابة "sandbox": true و"simulated": true و"sandbox_code".
curl -X POST "https://api.envoisms.ma/v1/verify/resend" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "vrf_…",
"channel": "sms"
}'{
"session_id": "vrf_7e2a9c4b1d3f5a7c9e0b2d4f",
"message_id": "msg_8c0e2b4d6f1a3c5e7b9d1f3a",
"to": "+212612345678",
"channel": "sms",
"status": "sent",
"resend_after_seconds": 30,
"expires_at": "2026-10-22T09:24:03.000Z"
}التحقق من رمز
يقارن الرمز الذي كتبه المستخدم برمز الجلسة. الرمز الصحيح يغلق الجلسة: والتحقق الثاني يعيد 404.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| session_id | string | مطلوب | الجلسة التي أعادها /v1/verify/send. |
| code | string | مطلوب | الرمز الذي كتبه المستخدم. |
- صلاحية مفتاح API المطلوبة: status.
- الأخطاء: 400 INVALID_CODE (تذكر الرسالة المحاولات المتبقية)، 429 MAX_ATTEMPTS (تصبح الجلسة failed)، 404 SESSION_NOT_FOUND — بما في ذلك للجلسة المنتهية أو التي سبق التحقق منها. لا يعيد /v1/verify أبداً EXPIRED_CODE.
curl -X POST "https://api.envoisms.ma/v1/verify/check" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "vrf_…",
"code": "483921"
}'{
"session_id": "vrf_7e2a9c4b1d3f5a7c9e0b2d4f",
"verified": true,
"verified_at": "2026-10-22T09:15:12.408Z"
}إلغاء جلسة
يغلق جلسة معلّقة: لم يعد الرمز المرسل مقبولاً. متاح أيضاً عبر POST /v1/verify/:id/void.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| session_id | string | مطلوب | الجلسة المراد إلغاؤها (تُحذف إذا كان المعرّف في المسار). |
- صلاحية مفتاح API المطلوبة: status.
- الأخطاء: 404 SESSION_NOT_FOUND و409 INVALID_STATE (جلسة سبق التحقق منها أو فشلت أو انتهت). وإذا كان لتطبيق Verify callback_url فيُبلَّغ (expired).
curl -X POST "https://api.envoisms.ma/v1/verify/void" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "vrf_…"
}'{
"session_id": "vrf_7e2a9c4b1d3f5a7c9e0b2d4f",
"status": "expired",
"voided": true
}عرض جلسة
يعيد حالة جلسة تحقق: الحالة والمحاولات والعد التنازلي قبل إعادة الإرسال ونتيجة تسليم كل إرسال.
- صلاحية مفتاح API المطلوبة: status.
- قيمة status هي pending أو verified أو failed أو expired. و404 NOT_FOUND إذا لم تكن الجلسة تابعة للحساب.
curl -X GET "https://api.envoisms.ma/v1/verify/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"id": "vrf_7e2a9c4b1d3f5a7c9e0b2d4f",
"session_id": "vrf_7e2a9c4b1d3f5a7c9e0b2d4f",
"to": "+212612345678",
"channel": "sms",
"status": "pending",
"attempts_used": 1,
"attempts_remaining": 2,
"max_attempts": 3,
"resend_available_in_seconds": 0,
"can_resend_now": true,
"expires_at": "2026-10-22T09:24:03.000Z",
"verified_at": null,
"created_at": "2026-10-22 09:14:03",
"app_id": null,
"app_name": null,
"attempted_channels": [],
"deliveries": [
{
"channel": "sms",
"kind": "send",
"status": "delivered",
"simulated": false,
"submitted_at": "2026-10-22 09:14:03",
"delivered_at": "2026-10-22 09:14:07",
"read_at": null,
"failed_at": null,
"error_code": null,
"error_message": null
}
],
"callback_delivered": null,
"cascade_triggered": false
}معرفة مشغّل رقم
يتحقق من رقم ويعيد بلده، ولرقم الجوال المغربي المشغّل الذي يخدمه مع احتساب نقل الأرقام. يُفوتَر كل استعلام ناجح؛ ولا يُفوتَر الرقم غير الصالح (400).
| المعلمة | النوع | الحالة | الوصف |
|---|---|---|---|
| number | string | اختياري | بديل عن معامل المسار: GET /v1/lookup?number=%2B212612345678. |
- صلاحية مفتاح API المطلوبة: send.
- الأخطاء: 400 MISSING_FIELD و400 INVALID_PHONE و402 INSUFFICIENT_BALANCE. مع مفتاح تجريبي تكون الإجابة حقيقية دون أي خصم، وتحمل "sandbox": true. قيمة type هي mobile أو unknown؛ وoperator تكون null إذا كان مجهولاً.
curl -X GET "https://api.envoisms.ma/v1/lookup/{number}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"id": "lkp_5d7f9b1c3e5a7c9e1b3d5f7a",
"number": "+212612345678",
"valid": true,
"country_iso": "MA",
"type": "mobile",
"operator": "Maroc Telecom",
"ported": false,
"cost": { "eur": 0.005, "mad": 0.055 }
}استعلام الشبكة (HLR)
يستعلم سجل المشغّل (HLR): هل الرقم قابل للوصول الآن، وأي شبكة تخدمه اليوم (مع احتساب نقل الأرقام)، وهل هو في التجوال، وهل هو جوال أم ثابت. يُفوتَر كل جواب من الشبكة أياً كان؛ ولا يُفوتَر الرقم غير الصالح (400) ولا الاستعلام غير المكتمل (502/503).
| المعلمة | النوع | الحالة | الوصف |
|---|---|---|---|
| number | string | اختياري | بديل عن معامل المسار: GET /v1/hlr?number=%2B212612345678. |
- صلاحية مفتاح API المطلوبة: send.
- قيمة status هي active (قابل للوصول) أو absent (معروف لكنه مطفأ أو خارج التغطية) أو inactive (خارج الخدمة) أو invalid أو unknown. يستغرق الجواب جولة شبكة واحدة (1 إلى 5 ثوانٍ). تشير source إلى مصدر الجواب: network = سجل المشغّل مستعلَماً مباشرة (تعكس status إمكانية الوصول)؛ portability = قاعدة نقل الأرقام لدى المشغّلين (الأرقام المغربية): المشغّل وحالة النقل والخط في الخدمة (active)، دون القول إن الهاتف مشغّل في هذه اللحظة. الأخطاء: 400 MISSING_FIELD و400 INVALID_PHONE و402 INSUFFICIENT_BALANCE و502 BAD_GATEWAY و503 HLR_UNAVAILABLE. مع مفتاح تجريبي يُحاكى الجواب مما تعرفه المنصة أصلاً، دون أي استعلام أو خصم، ويحمل "sandbox": true.
curl -X GET "https://api.envoisms.ma/v1/hlr/{number}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"id": "hlr_5d7f9b1c3e5a7c9e1b3d5f7a",
"number": "+212612345678",
"valid": true,
"status": "active",
"type": "mobile",
"country_iso": "MA",
"operator": "Maroc Telecom",
"mcc": "604",
"mnc": "01",
"ported": false,
"ported_operator": null,
"ported_mnc": null,
"roaming": false,
"roaming_country_iso": null,
"roaming_operator": null,
"roaming_mnc": null,
"source": "portability",
"cost": { "eur": 0.018, "mad": 0.20 }
}التحقق من صيغة رقم
يتحقق من صيغة رقم ويعيد صيغتيه المحلية والدولية وبلده وموقعه والمشغّل المخصِّص ونوع الخط. غير مُفوتَر.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| number | string | مطلوب | الرقم بصيغة محلية أو دولية. |
| country_code | string | اختياري | رمز الدولة ISO من حرفين (MA وFR…)، يُوصى به للرقم بصيغة محلية. |
- صلاحية مفتاح API المطلوبة: status.
- carrier هو المشغّل الذي خُصّصت له شريحة الأرقام، وليس بالضرورة من يخدم رقماً منقولاً: للمشغّل الفعلي استعمل GET /v1/lookup/:number. الأخطاء: 400 MISSING_FIELD؛ و503 LOOKUP_UNAVAILABLE عند تعذّر الخدمة مؤقتاً.
curl -X POST "https://api.envoisms.ma/v1/verify/lookup" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"number": "+212612345678"
}'{
"valid": true,
"number": "212612345678",
"local_format": "0612345678",
"international_format": "+212612345678",
"country_prefix": "+212",
"country_code": "MA",
"country_name": "Morocco",
"location": null,
"carrier": "Maroc Telecom",
"line_type": "mobile"
}قائمة المحادثات
يعيد سلاسل محادثات واتساب وSMS في الحساب، مع حالة نافذة الـ 24 ساعة لكل منها وعدادات إجمالية.
| المعلمة | النوع | الحالة | الوصف |
|---|---|---|---|
| limit | integer | اختياري | عدد النتائج: 50 افتراضياً و100 كحد أقصى. |
| offset | integer | اختياري | عدد النتائج التي يتم تخطيها (للتقسيم على صفحات). الافتراضي: 0. |
| filter | string | اختياري | all (الافتراضي) أو unread أو active_window أو needs_human. |
| channel | string | اختياري | all (الافتراضي) أو whatsapp أو sms. |
| phone_number_id | assigned_to | search | string | اختياري | رقم واتساب المتصل؛ المستشار المكلَّف؛ بحث في الرقم والاسم وآخر رسالة. |
- صلاحية مفتاح API المطلوبة: send.
curl -X GET "https://api.envoisms.ma/v1/conversations?limit=50" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "th_212612345678",
"phone": "212612345678",
"contact_name": "Yassine Alami",
"channel": "whatsapp",
"lead_status": "new",
"unread_count": 1,
"bot_muted": false,
"handling_agent": null,
"notes": null,
"tags": [],
"last_inbound_at": "2026-10-22 08:42:00",
"last_outbound_at": "2026-10-21 17:10:00",
"last_message_preview": "Bonjour, la veste existe en taille L ?",
"last_message_role": "customer",
"last_message_at": "2026-10-22 08:42:00",
"is_window_active": true,
"window_expires_at": "2026-10-23T08:42:00.000Z",
"window_seconds_remaining": 84960,
"waba_name": "Ma Boutique",
"waba_phone": "+212522000000",
"phone_number_id": "109283746501928"
}
],
"total": 1,
"limit": 50,
"offset": 0,
"stats": {
"total_threads": 1,
"unread_threads": 1,
"active_window_threads": 1,
"human_takeover_threads": 0
}
}عرض محادثة
يعيد سلسلة واحدة ورسائلها (500 كحد أقصى) من الأقدم إلى الأحدث، مع حالة نافذة الـ 24 ساعة.
- صلاحية مفتاح API المطلوبة: send.
- يقبل :phone الرقم مع + أو بدونه. قيمة role هي customer للرسالة المستلمة، وagent أو bot للرسالة المرسلة.
curl -X GET "https://api.envoisms.ma/v1/conversations/{phone}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"thread": {
"id": "th_212612345678",
"phone": "212612345678",
"contact_name": "Yassine Alami",
"channel": "whatsapp",
"lead_status": "new",
"unread_count": 0,
"bot_muted": true,
"handling_agent": "Salma",
"notes": null,
"tags": [],
"last_inbound_at": "2026-10-22 08:42:00",
"last_outbound_at": "2026-10-22 08:45:10",
"is_window_active": true,
"window_expires_at": "2026-10-23T08:42:00.000Z",
"window_seconds_remaining": 84960,
"window": {
"is_window_active": true,
"window_expires_at": "2026-10-23T08:42:00.000Z",
"window_seconds_remaining": 84960
},
"waba_name": "Ma Boutique",
"waba_phone": "+212522000000",
"phone_number_id": "109283746501928"
},
"messages": [
{
"id": "wamsg_1c3e5a7c9e1b3d5f7a9c1e3b",
"phone": "212612345678",
"role": "customer",
"message": "Bonjour, la veste existe en taille L ?",
"wamid": "wamid.HBgLMjEyNjEyMzQ1Njc4FQIAEhgg",
"status": "read",
"media_type": "text",
"media_url": null,
"metadata": {},
"read_at": "2026-10-22 08:43:00",
"created_at": "2026-10-22 08:42:00"
}
]
}الرد في محادثة
يرسل رد مستشار (نص أو وسائط أو قالب) ويوقف المساعد الآلي مؤقتاً في هذه السلسلة. يمر الإرسال بنفس مسار POST /v1/messages: تُطبَّق التسعيرة وقائمة إلغاء الاشتراك والخصم. يعيد 201.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| message | string | اختياري | نص الرد أو شرح الوسائط. إلزامي في غياب قالب أو وسائط (400 MISSING_MESSAGE). |
| channel | string | اختياري | "whatsapp" (الافتراضي) أو "sms". |
| template_id | template_name | template_language | variables | mixed | اختياري | قالب واتساب معتمد يُرسل بدل النص الحر (بالمعرّف أو بالاسم واللغة)، مع قيمه (مصفوفة أو كائن). |
| media_url | media_type | filename | string | اختياري | الوسائط المرفقة: رابط https (أو data URI)، النوع image أو document، واسم ملف المستند. |
| phone_number_id | string | اختياري | رقم واتساب المتصل الذي يرد. |
- صلاحية مفتاح API المطلوبة: send.
- تتطلب رسالة واتساب الحرة أن يكون جهة الاتصال قد راسلك خلال آخر 24 ساعة (وإلا 400 OUT_OF_24H_WINDOW)؛ ويمكن إرسال القالب المعتمد في أي وقت. أخطاء أخرى: 403 WHATSAPP_NOT_CONNECTED و404 TEMPLATE_NOT_FOUND و400 TEMPLATE_NOT_APPROVED.
curl -X POST "https://api.envoisms.ma/v1/conversations/{phone}/messages" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "Thanks, we will call you back today."
}'{
"ok": true,
"message": {
"id": "msg_9e1b3d5f7a9c1e3b5d7f9a1c",
"phone": "212612345678",
"role": "agent",
"message": "Oui, la taille L est disponible.",
"wamid": "wamid.HBgLMjEyNjEyMzQ1Njc4FQIAERgS",
"status": "sent",
"channel": "whatsapp",
"media_type": "text",
"metadata": { "agent": "Salma" },
"created_at": "2026-10-22T08:45:10.221Z"
}
}قائمة العملاء المحتملين المؤهلين
يعيد العملاء المحتملين الذين سجّلهم روبوت واتساب الخاص بك، من الأحدث إلى الأقدم، مع درجتهم (0 إلى 100) وحالتهم. format=csv ينزّل القائمة نفسها.
| المعلمة | النوع | الحالة | الوصف |
|---|---|---|---|
| limit | integer | اختياري | عدد النتائج: 100 افتراضياً و500 كحد أقصى. |
| status | string | اختياري | new أو contacted أو qualified أو Fournisseur أو won أو lost. |
| preset | string | اختياري | قطاع الروبوت (مثلاً medical_equipment)؛ وsupplier يجمع طلبات الموردين. |
| search | string | اختياري | بحث في الرقم والاسم والفئة والملاحظات. |
| format | string | اختياري | json (الافتراضي) أو csv. |
- أي مفتاح API صالح: لا تُشترط صلاحية بعينها.
- لا يوجد تقسيم عبر offset: يحدّ limit القائمة. تُحسب metrics على العملاء المُعادين؛ ويعدّ hot من بلغت درجتهم 80 فأكثر.
curl -X GET "https://api.envoisms.ma/v1/qualified-leads?limit=50" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"ok": true,
"leads": [
{
"id": "lead_3a5c7e9b1d3f5a7c9e1b3d5f",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"phone_number_id": "109283746501928",
"contact_phone": "+212612345678",
"contact_name": "Cabinet Benjelloun",
"preset": "medical_equipment",
"category": "Échographe",
"bant_score": 85,
"urgency": "immediate",
"budget": "200 000 - 300 000 MAD",
"authority": "decision_maker",
"notes": null,
"source_type": "direct_inbound",
"status": "new",
"created_at": "2026-10-21 09:15:00",
"updated_at": "2026-10-21 09:15:00"
}
],
"metrics": { "total": 1, "hot": 1, "average_score": 85 }
}تحديث عميل محتمل
يغيّر حالة العميل المحتمل أو ملاحظاته أو اسمه. يعيد العميل بعد التحديث.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| status | string | اختياري | new أو contacted أو qualified أو Fournisseur أو won أو lost (وإلا 400 INVALID_STATUS). |
| notes | string | اختياري | ملاحظات متابعة داخلية. |
| contact_name | string | اختياري | اسم جهة الاتصال أو المؤسسة. |
| value_minor | currency | integer | string | اختياري | قيمة الصفقة بأصغر وحدة نقدية وعملتها، تُرسلان إلى Meta عند الانتقال إلى won. |
- أي مفتاح API صالح: لا تُشترط صلاحية بعينها.
- يُبلَّغ Meta بالانتقال إلى qualified أو won (QualifiedLead وPurchase) إذا جاء العميل من إعلان يقود إلى واتساب؛ وذلك عند تغيّر فعلي في الحالة فقط. و404 LEAD_NOT_FOUND إذا لم يكن العميل تابعاً للحساب.
curl -X PATCH "https://api.envoisms.ma/v1/qualified-leads/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "contacted",
"notes": "Called back on 22/10"
}'{
"ok": true,
"lead": {
"id": "lead_3a5c7e9b1d3f5a7c9e1b3d5f",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"phone_number_id": "109283746501928",
"contact_phone": "+212612345678",
"contact_name": "Cabinet Benjelloun",
"preset": "medical_equipment",
"category": "Échographe",
"bant_score": 85,
"urgency": "immediate",
"budget": "200 000 - 300 000 MAD",
"authority": "decision_maker",
"notes": "Rendez-vous fixé jeudi.",
"source_type": "direct_inbound",
"status": "contacted",
"created_at": "2026-10-21 09:15:00",
"updated_at": "2026-10-22 10:02:31",
"user_id": null
}
}قائمة جهات الاتصال
يعيد جهات الاتصال في الحساب، مع البحث والتصفية حسب القائمة.
- صلاحية مفتاح API المطلوبة: contacts.
curl -X GET "https://api.envoisms.ma/v1/contacts?limit=50" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "ctc_2d4f6a8c0e1b3d5f7a9c1e3b",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"phone": "+212612345678",
"name": "Karim Bennani",
"email": "karim@example.ma",
"custom1": "VIP",
"custom2": null,
"custom3": null,
"opted_out": 0,
"opted_out_at": null,
"created_at": "2026-10-10 14:20:00"
}
],
"limit": 100,
"offset": 0,
"total": 1
}إنشاء جهة اتصال أو تحديثها
ينشئ جهة اتصال أو يحدّث الجهة التي تملك هذا الرقم (يُستبدل الاسم والبريد والحقول المخصصة). يعيد 201 مع جهة الاتصال.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| phone | string | مطلوب | الرقم بصيغة E.164 (وإلا 400 INVALID_PHONE). |
| name | email | string | اختياري | اسم جهة الاتصال وبريدها الإلكتروني. |
| list_id | string | اختياري | يضيف جهة الاتصال أيضاً إلى هذه القائمة (404 LIST_NOT_FOUND إن لم تكن لك). |
| custom_fields | object | اختياري | {"custom1"، "custom2"، "custom3"}: ثلاثة حقول حرة تُستعمل في الحملات. |
- صلاحية مفتاح API المطلوبة: contacts.
curl -X POST "https://api.envoisms.ma/v1/contacts" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "+212612345678",
"name": "Karim Bennani",
"email": "karim@example.ma"
}'{
"id": "ctc_2d4f6a8c0e1b3d5f7a9c1e3b",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"phone": "+212612345678",
"name": "Karim Bennani",
"email": "karim@example.ma",
"custom1": "VIP",
"custom2": null,
"custom3": null,
"opted_out": 0,
"opted_out_at": null,
"created_at": "2026-10-10 14:20:00"
}استيراد جهات الاتصال
يضع استيراد جهات اتصال في قائمة الانتظار، بصيغة JSON أو ملف CSV، ويعيد 202 فوراً؛ ثم يجري الاستيراد على دفعات من 100.
- صلاحية مفتاح API المطلوبة: contacts.
- تُعطى الاستجابة عند الوضع في قائمة الانتظار: يعدّ total الأسطر المستلمة، ويكرره imported كما هو. تُستبعد الأرقام غير الصالحة أثناء الاستيراد؛ تحقق من النتيجة عبر GET /v1/contacts. أنواع المحتوى الأخرى: 415 UNSUPPORTED_MEDIA_TYPE.
curl -X POST "https://api.envoisms.ma/v1/contacts/import" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contacts": [
{
"phone": "+212611111111",
"name": "Karim"
},
{
"phone": "+212622222222",
"name": "Salma"
}
]
}'{
"jobId": "job_4b6d8f0a2c4e6b8d0f2a4c6e",
"total": 150,
"imported": 150,
"skipped": 0,
"status": "queued"
}حذف جهة اتصال
يحذف جهة اتصال نهائياً. 404 NOT_FOUND إن لم تكن موجودة.
- صلاحية مفتاح API المطلوبة: contacts.
curl -X DELETE "https://api.envoisms.ma/v1/contacts/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"deleted": true,
"id": "ctc_2d4f6a8c0e1b3d5f7a9c1e3b"
}قائمة قوائم جهات الاتصال
يعيد قوائم جهات الاتصال في الحساب مع عدد أعضائها.
- صلاحية مفتاح API المطلوبة: contacts.
curl -X GET "https://api.envoisms.ma/v1/contacts/lists" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "lst_f84b2d6a8c0e1f3b5d7a9c2e",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"name": "Clients fidèles",
"description": "Clients ayant commandé au moins deux fois",
"count": 1420,
"created_at": "2026-09-15 09:00:00"
}
]
}إنشاء قائمة
ينشئ قائمة جهات اتصال فارغة تُملأ عبر POST /v1/contacts أو /v1/contacts/import. يعيد 201.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| name | string | مطلوب | اسم القائمة. |
| description | string | اختياري | وصف حر. |
- صلاحية مفتاح API المطلوبة: contacts.
curl -X POST "https://api.envoisms.ma/v1/contacts/lists" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Newsletter",
"description": "Customers who subscribed at checkout"
}'{
"id": "lst_f84b2d6a8c0e1f3b5d7a9c2e",
"name": "Clients fidèles",
"description": "Clients ayant commandé au moins deux fois",
"count": 0
}إعادة تسمية قائمة
يستبدل اسم القائمة ووصفها. 404 NOT_FOUND إن لم تكن موجودة.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| name | string | مطلوب | الاسم الجديد. |
| description | string | اختياري | الوصف الجديد؛ يُمحى إذا لم يُرسل. |
- صلاحية مفتاح API المطلوبة: contacts.
curl -X POST "https://api.envoisms.ma/v1/contacts/lists/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"id": "lst_f84b2d6a8c0e1f3b5d7a9c2e",
"name": "Clients fidèles 2026",
"description": null
}حذف قائمة
يحذف قائمة وعضوياتها؛ وتبقى جهات الاتصال نفسها. يُرفض (400 LIST_IN_USE) ما دامت حملة مجدولة أو جارية أو متوقفة مؤقتاً تستعملها.
- صلاحية مفتاح API المطلوبة: contacts.
curl -X DELETE "https://api.envoisms.ma/v1/contacts/lists/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"deleted": true,
"id": "lst_f84b2d6a8c0e1f3b5d7a9c2e"
}قائمة إلغاءات الاشتراك
يعيد الأرقام التي ألغت اشتراكها في إرسالياتك. لا تُرسل إليها أي رسالة (403 OPTED_OUT).
- صلاحية مفتاح API المطلوبة: optouts.
curl -X GET "https://api.envoisms.ma/v1/optouts?limit=50" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{ "phone": "+212611111111", "opted_out_at": "2026-10-01 12:00:00" }
],
"limit": 100,
"offset": 0,
"total": 1
}إضافة إلغاء اشتراك
يضيف رقماً إلى قائمة إلغاء الاشتراك الخاصة بك. يعيد 201.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| phone | string | مطلوب | الرقم بصيغة E.164. |
- صلاحية مفتاح API المطلوبة: optouts.
curl -X POST "https://api.envoisms.ma/v1/optouts" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "+212612345678"
}'{
"phone": "+212611111111",
"opted_out": true
}إزالة إلغاء اشتراك
يزيل رقماً من قائمة إلغاء الاشتراك. لا تفعل ذلك إلا بموافقة جديدة من الشخص، تُسجَّل عبر POST /v1/consents.
- صلاحية مفتاح API المطلوبة: optouts.
curl -X DELETE "https://api.envoisms.ma/v1/optouts/{phone}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"phone": "+212611111111",
"opted_out": false
}قائمة أحداث الموافقة
سجل موافقات بالإضافة فقط (القانون 09-08): كل موافقة وكل سحب حدث مؤرَّخ بمصدره ودليله. وتسجّل المنصة بنفسها الكلمتين STOP/START المستلمتين وأول محادثة يفتحها الزبون.
| المعلمة | النوع | الحالة | الوصف |
|---|---|---|---|
| limit | integer | اختياري | عدد النتائج: 100 افتراضياً و500 كحد أقصى. |
| offset | integer | اختياري | عدد النتائج التي يتم تخطيها (للتقسيم على صفحات). الافتراضي: 0. |
| phone | string | اختياري | رقم E.164. |
| purpose | string | اختياري | marketing أو transactional أو otp أو service. |
| status | string | اختياري | granted أو withdrawn. |
| from | to | string | اختياري | حدود ISO 8601 على recorded_at. |
| format | string | اختياري | csv: ينزّل المجموعة المصفاة كاملة. |
- صلاحية مفتاح API المطلوبة: contacts.
curl -X GET "https://api.envoisms.ma/v1/consents?limit=50" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "cns_9f2a4c6e8b0d1f3a5c7e9b2d",
"phone": "+212612345678",
"channel": "whatsapp",
"purpose": "marketing",
"status": "withdrawn",
"source": "whatsapp_keyword",
"evidence": { "keyword": "stop", "lang": "fr" },
"recorded_at": "2026-10-05T09:00:00.000Z",
"created_at": "2026-10-05T09:00:00.000Z"
}
],
"limit": 100,
"offset": 0,
"total": 1
}تسجيل حدث موافقة
يسجّل أن شخصاً منح موافقته أو سحبها، مع الدليل الذي بحوزتك. ويضع سحب الموافقة التسويقية الرقم فوراً في قائمة إلغاء الاشتراك. يعيد 201.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| phone | string | مطلوب | الرقم بصيغة E.164. |
| purpose | string | مطلوب | marketing أو transactional أو otp أو service. |
| status | string | اختياري | granted (الافتراضي) أو withdrawn. |
| channel | string | اختياري | whatsapp أو sms أو any (الافتراضي). |
| source | string | اختياري | api (الافتراضي) أو form أو import أو dashboard. مصادر الكلمات المفتاحية محجوزة للمنصة. |
| evidence | object | اختياري | كائن JSON حر أقل من 4096 بايت: النص المعروض، الرابط، عنوان IP، المرجع. |
| recorded_at | string | اختياري | تاريخ ISO 8601 للفعل (افتراضياً الآن)؛ يمكن تأريخه بأثر رجعي عند الاستيراد، لكن ليس في المستقبل. |
- صلاحية مفتاح API المطلوبة: contacts.
- الأخطاء: 400 INVALID_PHONE وINVALID_PURPOSE وINVALID_STATUS وINVALID_CHANNEL وINVALID_SOURCE وINVALID_EVIDENCE وINVALID_DATE.
curl -X POST "https://api.envoisms.ma/v1/consents" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "+212612345678",
"purpose": "marketing",
"status": "granted",
"channel": "whatsapp",
"source": "form",
"evidence": {
"form": "checkout",
"ip": "196.200.1.4"
}
}'{
"id": "cns_1b3d5f7a9c2e4b6d8f0a2c4e",
"phone": "+212612345678",
"channel": "any",
"purpose": "marketing",
"status": "granted",
"source": "form",
"evidence": { "text": "J’accepte de recevoir les offres par WhatsApp", "url": "https://example.ma/inscription" },
"recorded_at": "2026-10-01T10:00:00.000Z",
"created_at": "2026-10-01T10:00:00.000Z"
}حالة الموافقة وسجلها لرقم
يعيد الحالة الحالية لكل غرض (من أحدث حدث؛ unknown بلا سجل، ولا تُفترض الموافقة أبداً)، ووجود الرقم في قائمة إلغاء الاشتراك، والسجل (500 حدث كحد أقصى).
- صلاحية مفتاح API المطلوبة: contacts.
curl -X GET "https://api.envoisms.ma/v1/consents/{phone}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"phone": "+212612345678",
"opted_out": true,
"opted_out_at": "2026-10-05T09:00:00.000Z",
"state": {
"marketing": { "status": "withdrawn", "channel": "whatsapp", "source": "whatsapp_keyword", "recorded_at": "2026-10-05T09:00:00.000Z", "record_id": "cns_9f2a4c6e8b0d1f3a5c7e9b2d" },
"transactional": { "status": "unknown" },
"otp": { "status": "unknown" },
"service": { "status": "granted", "channel": "whatsapp", "source": "whatsapp_inbound", "recorded_at": "2026-07-01T09:00:00.000Z", "record_id": "cns_0a2c4e6b8d1f3a5c7e9b0d2f" }
},
"history": []
}قائمة الحملات
يعيد حملات الحساب من الأحدث إلى الأقدم، مع عداداتها.
- صلاحية مفتاح API المطلوبة: campaigns.
- الحالات: draft وscheduled وrunning وpaused وcompleted وfailed وcancelled.
curl -X GET "https://api.envoisms.ma/v1/campaigns?limit=50" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "cmp_8d2a4c6e1b3f5a7c9e0b2d4f",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"name": "Soldes d’automne",
"channel": "sms",
"list_id": "lst_f84b2d6a8c0e1f3b5d7a9c2e",
"template_id": null,
"body": "Bonjour {{name}}, -30% sur la collection avec le code AUT30.",
"sender_id": "MABOUTIQUE",
"status": "completed",
"scheduled_at": null,
"started_at": "2026-10-15 10:00:00",
"completed_at": "2026-10-15 10:06:12",
"total_count": 1420,
"sent_count": 1412,
"delivered_count": 1380,
"failed_count": 8,
"cost_eur": 102.24,
"cost_mad": 1124.64,
"created_at": "2026-10-14 16:30:00",
"buttons": null,
"template_variables": null
}
],
"total": 1,
"limit": 50,
"offset": 0
}إنشاء حملة
ينشئ حملة كمسودة، أو مجدولة إذا أُعطي scheduled_at. لا يُرسل شيء قبل POST /v1/campaigns/:id/send. يعيد 201.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| name | string | مطلوب | اسم الحملة. |
| body | string | اختياري | (SMS) نص الرسالة. يلزم body أو template_id. |
| template_id | string | اختياري | قالب من الحساب. إلزامي على واتساب، حيث يجب أن تكون Meta قد اعتمدته (400 TEMPLATE_NOT_APPROVED). |
| channel | string | اختياري | "sms" (الافتراضي) أو "whatsapp" (يتطلب رقم واتساب للأعمال متصلاً، وإلا 403 WHATSAPP_NOT_CONNECTED). |
| list_id | string | اختياري | قائمة المستلمين؛ لازمة لإطلاق الحملة (404 LIST_NOT_FOUND إن لم تكن لك). |
| sender_id | string | اختياري | SMS: مُعرّف المرسل (نفس قواعد POST /v1/messages؛ الافتراضي EnvoiSMS). واتساب: الرقم المتصل الذي يرسل (افتراضياً رقمك الافتراضي؛ و400 INVALID_WABA_SENDER إن لم يكن لك). |
| scheduled_at | string | اختياري | تاريخ ISO 8601؛ يجعل حالة الحملة scheduled. |
| template_variables | object | اختياري | (واتساب) قيمة كل معامل في القالب حسب المفتاح. المفتاح "1" و"2"… أو اسم المتغير: حقل من جهة الاتصال (name وphone وemail وcustom1 وcustom2 وcustom3) أو نص ثابت مسبوق بـ "static:". و"_header_media_url": رابط https لوسائط الترويسة (إلزامي لترويسة صورة أو فيديو أو مستند). و"_button_<i>": الجزء المتغير من زر الرابط ذي الفهرس i (ابتداءً من 0). وبلا قيمة يقرأ {{1}} الحقل name و{{2}} الحقل custom1 و{{3}} الحقل custom2. |
| consent_attested | boolean | اختياري | (واتساب، قالب marketing) true: تشهد بأن كل جهة اتصال في القائمة وافقت على تلقي رسائل واتساب منك. إلزامي لقالب marketing (وإلا 400 CONSENT_ATTESTATION_REQUIRED)؛ ويُسجَّل تاريخ الإقرار. |
| buttons | array | اختياري | (واتساب) أزرار بنفس صيغة POST /v1/messages. مرفوضة في SMS (400 BUTTONS_NOT_SUPPORTED). |
- صلاحية مفتاح API المطلوبة: campaigns.
- واتساب: يُتحقَّق عند الإنشاء من كل ما يتطلبه القالب لكل جهة اتصال؛ ويُسمّى الناقص في 400 MISSING_TEMPLATE_PARAMS. أخطاء أخرى: 400 MISSING_TEMPLATE و404 TEMPLATE_NOT_FOUND.
curl -X POST "https://api.envoisms.ma/v1/campaigns" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer sale",
"channel": "sms",
"body": "Summer sale: -20% until Sunday. Reply STOP to unsubscribe.",
"list_id": "lst_…"
}'{
"id": "cmp_8d2a4c6e1b3f5a7c9e0b2d4f",
"status": "draft"
}عرض حملة
يعيد حملة واحدة وحالتها وعدادات إرسالها. وإلا 404 NOT_FOUND.
- صلاحية مفتاح API المطلوبة: campaigns.
curl -X GET "https://api.envoisms.ma/v1/campaigns/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"id": "cmp_8d2a4c6e1b3f5a7c9e0b2d4f",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"name": "Nouvelle collection",
"channel": "whatsapp",
"list_id": "lst_f84b2d6a8c0e1f3b5d7a9c2e",
"template_id": "tpl_8e2a4c6b1d3f5a7c9e0b2d4f",
"body": null,
"sender_id": "+212522000000",
"status": "running",
"scheduled_at": null,
"started_at": "2026-10-22 10:00:00",
"completed_at": null,
"total_count": 1420,
"sent_count": 840,
"delivered_count": 790,
"failed_count": 3,
"cost_eur": 0,
"cost_mad": 0,
"created_at": "2026-10-21 18:12:40",
"buttons": null,
"template_variables": "{\"1\":\"name\"}"
}تعديل حملة
يعدّل حملة مسودة أو مجدولة؛ لا يتغير إلا ما ترسله. يعيد الحملة بعد التحديث.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| name | body | sender_id | scheduled_at | string | اختياري | اسم أو نص أو مرسل أو تاريخ جدولة جديد (ISO 8601). القيمة الفارغة تترك الحقل دون تغيير. |
- صلاحية مفتاح API المطلوبة: campaigns.
- 409 CAMPAIGN_NOT_EDITABLE إذا لم تكن الحملة draft ولا scheduled؛ و404 NOT_FOUND إن لم تكن موجودة.
curl -X PATCH "https://api.envoisms.ma/v1/campaigns/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scheduled_at": "2026-11-01T09:00:00Z"
}'{
"id": "cmp_8d2a4c6e1b3f5a7c9e0b2d4f",
"account_id": "acct_7b2e4c6a8d0f1e3b5c7a9d2f",
"name": "Soldes d’automne — relance",
"channel": "sms",
"list_id": "lst_f84b2d6a8c0e1f3b5d7a9c2e",
"template_id": null,
"body": "Dernier jour : -30% avec le code AUT30.",
"sender_id": "MABOUTIQUE",
"status": "scheduled",
"scheduled_at": "2026-10-25T09:00:00Z",
"started_at": null,
"completed_at": null,
"total_count": 0,
"sent_count": 0,
"delivered_count": 0,
"failed_count": 0,
"cost_eur": 0,
"cost_mad": 0,
"created_at": "2026-10-22 11:05:00",
"buttons": null,
"template_variables": null
}إلغاء حملة
يجعل حالة الحملة cancelled؛ وتبقى قابلة للعرض. يجب إيقاف الحملة الجارية مؤقتاً أولاً (409 CAMPAIGN_RUNNING).
- صلاحية مفتاح API المطلوبة: campaigns.
curl -X DELETE "https://api.envoisms.ma/v1/campaigns/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"deleted": true,
"id": "cmp_8d2a4c6e1b3f5a7c9e0b2d4f"
}إطلاق حملة
يطلق الحملة نحو أعضاء قائمتها الذين لم يلغوا اشتراكهم. يجري الإرسال في الخلفية؛ تابعه عبر GET /v1/campaigns/:id. يعيد 202.
- صلاحية مفتاح API المطلوبة: campaigns.
- الأخطاء: 400 ALREADY_RUNNING و400 MISSING_LIST و400 EMPTY_LIST (لا مستلم مؤهل) و400 MISSING_BODY و404 NOT_FOUND. تُسعَّر كل رسالة وتُخصم كإرسال فردي.
curl -X POST "https://api.envoisms.ma/v1/campaigns/{id}/send" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"id": "cmp_8d2a4c6e1b3f5a7c9e0b2d4f",
"status": "running",
"total": 1420
}إيقاف حملة مؤقتاً
يوقف حملة جارية مؤقتاً. 409 NOT_RUNNING إن لم تكن جارية.
- صلاحية مفتاح API المطلوبة: campaigns.
curl -X POST "https://api.envoisms.ma/v1/campaigns/{id}/pause" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"paused": true,
"id": "cmp_8d2a4c6e1b3f5a7c9e0b2d4f"
}استئناف حملة
يستأنف حملة متوقفة مؤقتاً من حيث توقفت. 409 NOT_PAUSED إن لم تكن متوقفة.
- صلاحية مفتاح API المطلوبة: campaigns.
curl -X POST "https://api.envoisms.ma/v1/campaigns/{id}/resume" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"resumed": true,
"id": "cmp_8d2a4c6e1b3f5a7c9e0b2d4f"
}قائمة Webhooks
يعيد Webhooks النشطة لديك. لا يظهر السر إلا مقنَّعاً (secret_hint)؛ ولا يُعرض كاملاً إلا عند الإنشاء والتدوير.
- صلاحية مفتاح API المطلوبة: webhooks.
curl -X GET "https://api.envoisms.ma/v1/webhooks" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "whk_5f2b7d9a1c3e5b7d9f1a3c5e",
"name": "Webhook Endpoint",
"target_type": "custom",
"format": "standard",
"url": "https://example.ma/hooks/envoisms",
"events": "[\"message.delivered\",\"message.failed\"]",
"active": 1,
"last_triggered_at": "2026-10-22 09:14:09",
"last_status": 200,
"created_at": "2026-10-01 10:00:00",
"secret_hint": "••••9c2e"
}
]
}إنشاء Webhook
يسجّل رابط HTTPS يستقبل الأحداث المختارة. يعيد 201 مع سر التوقيع، ويُعرض هذه المرة فقط.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| url | string | مطلوب | رابط HTTPS عام (وإلا 400 INVALID_URL). |
| events | string[] | اختياري | message.sent وmessage.delivered وmessage.read وmessage.failed وmessage.undeliverable وmessage.fallback وmessage.inbound وmessage.reaction وcontact.optout وcontact.optin وlead.qualified؛ وأحرف البدل "message.*" و"*". الافتراضي: ["message.delivered"، "message.failed"]. |
| secret | string | اختياري | سر التوقيع؛ يُولَّد (sec_…) إن لم يُرسل. |
| name | format | string | اختياري | تسمية؛ والصيغة standard ({"event"، "data"، "timestamp"}) أو flat (حقول data في المستوى الأعلى أيضاً، لجدول بيانات). |
- صلاحية مفتاح API المطلوبة: webhooks.
- التوقيع: يحمل كل إرسال X-EnvoiSMS-Signature-256: t=<وقت يونكس>,v1=<HMAC-SHA256 ست عشري لـ "t.body" بالسر>، وX-EnvoiSMS-Event. ارفض كل طابع زمني أقدم من 5 دقائق. يُعاد الفشل (5xx أو 429 أو الشبكة) بعد 60 ثانية ثم 10 دقائق ثم ساعة: 4 محاولات إجمالاً.
- يتوقف عدد Webhooks النشطة على الباقة (Starter 2، Business 5، Pro 15، Enterprise 50)؛ وما زاد 403 LIMIT_EXCEEDED.
curl -X POST "https://api.envoisms.ma/v1/webhooks" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.ma/webhooks/envoisms",
"events": [
"message.delivered",
"message.failed",
"message.inbound"
]
}'{
"id": "whk_5f2b7d9a1c3e5b7d9f1a3c5e",
"name": "Webhook Endpoint",
"target_type": "custom",
"format": "standard",
"url": "https://example.ma/hooks/envoisms",
"events": ["message.delivered", "message.failed"],
"secret": "sec_3d5f7a9c1e3b5d7f9a1c9c2e",
"active": true
}تعديل Webhook
يعدّل رابط Webhook أو أحداثه أو اسمه أو صيغته أو حالة تفعيله؛ لا يتغير إلا ما ترسله.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| url | events | name | format | mixed | اختياري | نفس قواعد الإنشاء. |
| active | boolean | اختياري | القيمة false توقف الإرسال إلى هذا Webhook. |
- صلاحية مفتاح API المطلوبة: webhooks.
curl -X PATCH "https://api.envoisms.ma/v1/webhooks/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"active": false
}'{
"updated": true,
"id": "whk_5f2b7d9a1c3e5b7d9f1a3c5e"
}حذف Webhook
يعطّل Webhook: لا يُرسل إليه أي حدث بعد ذلك ويختفي من القائمة.
- صلاحية مفتاح API المطلوبة: webhooks.
curl -X DELETE "https://api.envoisms.ma/v1/webhooks/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"deleted": true,
"id": "whk_5f2b7d9a1c3e5b7d9f1a3c5e"
}اختبار Webhook
يرسل حدثاً نموذجياً موقَّعاً كحدث حقيقي إلى رابط Webhook. يعيد 202 مع النموذج المرسل.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| event | string | اختياري | اسم الحدث المراد محاكاته (الافتراضي: lead.qualified). البيانات النموذجية رسالة واردة مع message.inbound، وعميل مؤهل في غير ذلك. |
- صلاحية مفتاح API المطلوبة: webhooks.
curl -X POST "https://api.envoisms.ma/v1/webhooks/{id}/test" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"event": "message.delivered"
}'{
"queued": true,
"id": "whk_5f2b7d9a1c3e5b7d9f1a3c5e",
"event": "message.inbound",
"sample": {
"event": "message.inbound",
"data": {
"message_id": "msg_demo_k3x9f2ab",
"from": "+212612345678",
"to": "+212708789095",
"channel": "whatsapp",
"text": "Bonjour, je souhaite des informations sur vos tarifs et solutions.",
"contact_name": "Karim Bennani",
"timestamp": "2026-10-22T09:30:00.000Z"
},
"timestamp": "2026-10-22T09:30:00.000Z"
}
}تدوير سر Webhook
يستبدل سر التوقيع ويعيد السر الجديد هذه المرة فقط. تبقى الإرسالات الموجودة في قائمة الانتظار موقَّعة بالسر القديم: اقبل الاثنين بضع دقائق.
- صلاحية مفتاح API المطلوبة: webhooks.
curl -X POST "https://api.envoisms.ma/v1/webhooks/{id}/rotate-secret" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"id": "whk_5f2b7d9a1c3e5b7d9f1a3c5e",
"secret": "sec_7a9c1e3b5d7f9a1c3e5b0d2f",
"rotated": true
}سجل إرسالات Webhook
يعيد آخر 100 محاولة إرسال من الأحدث إلى الأقدم، مع النص المرسل واستجابة خادمك.
| المعلمة | النوع | الحالة | الوصف |
|---|---|---|---|
| webhook_id | string | اختياري | إرسالات هذا Webhook فقط. |
- صلاحية مفتاح API المطلوبة: webhooks.
curl -X GET "https://api.envoisms.ma/v1/webhooks/logs" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "0b6f3c2e-8a4d-4f1b-9c7e-2d5a1f8e6b30",
"webhook_id": "whk_5f2b7d9a1c3e5b7d9f1a3c5e",
"event": "message.delivered",
"url": "https://example.ma/hooks/envoisms",
"payload": "{\"event\":\"message.delivered\",\"data\":{\"id\":\"msg_3f9a1c7e5b2d4a6f8e0c1b3d\",\"to\":\"+212612345678\",\"channel\":\"sms\",\"status\":\"delivered\"},\"timestamp\":\"2026-10-22T09:14:09.120Z\"}",
"status_code": 500,
"error_message": null,
"attempt": 1,
"delivered": 0,
"created_at": "2026-10-22 09:14:09"
}
],
"max_attempts": 4
}إعادة إرسال Webhook
يعيد إرسال حدث مسجَّل — الحدث نفسه والبيانات نفسها بتوقيع جديد — إلى الرابط الحالي لـ Webhook مع المحاولات المعتادة. يعيد 202.
- صلاحية مفتاح API المطلوبة: webhooks.
- 404 NOT_FOUND إذا كان السجل غير معروف أو حُذف Webhook الخاص به؛ و422 REPLAY_UNAVAILABLE إذا لم يُحفظ النص المرسل.
curl -X POST "https://api.envoisms.ma/v1/webhooks/logs/{log_id}/replay" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"queued": true,
"replay_of": "0b6f3c2e-8a4d-4f1b-9c7e-2d5a1f8e6b30",
"webhook_id": "whk_5f2b7d9a1c3e5b7d9f1a3c5e",
"event": "message.delivered"
}قائمة Webhooks الواردة
يعيد Webhooks الواردة لديك: روابط التقاط تحوّل نداءً من أداة خارجية (استمارة، Zapier، Make…) إلى رسالة مُرسلة.
- أي مفتاح API صالح: لا تُشترط صلاحية بعينها.
curl -X GET "https://api.envoisms.ma/v1/inbound-webhooks" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "inwh_9a8b7c6d5e4f3a2b1c0d9e8f",
"name": "Formulaire de contact",
"channel": "whatsapp",
"template_id": "tpl_4c1e9a7b3d5f2e8a6c0b1d3f",
"active": 1,
"created_at": "2026-10-12 14:00:00"
}
]
}إنشاء Webhook وارد
ينشئ رابط التقاط POST /v1/inbound-webhooks/catch/:id: كل نداء JSON يستقبله يرسل رسالة إلى الرقم المقروء من النص. يعيد 201.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| name | string | مطلوب | تسمية المصدر. |
| channel | string | مطلوب | sms أو whatsapp. |
| mapping_config | object | مطلوب | {"to_path": "contact.phone"، "variables": {"1": "contact.name"}}: مسار الرقم في JSON المستلم، ومسار كل متغير في القالب. |
| template_id | string | اختياري | القالب المراد إرساله (المعرّف أو الاسم). |
- أي مفتاح API صالح: لا تُشترط صلاحية بعينها.
- لا يتطلب رابط الالتقاط مفتاحاً: معرّفه بمثابة السر، فلا تنشره. يقبل 64 كيلوبايت كحد أقصى و120 نداءً في الدقيقة، ويعيد 202 {"success": true، "messageId"} ويخصم من رصيدك كإرسال عادي. الحذف عبر DELETE /v1/inbound-webhooks/:id.
curl -X POST "https://api.envoisms.ma/v1/inbound-webhooks" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Website form",
"channel": "whatsapp",
"mapping_config": {
"to_path": "phone"
}
}'{
"id": "inwh_9a8b7c6d5e4f3a2b1c0d9e8f"
}الاطلاع على الرصيد
يعيد الرصيد المسبق الدفع بالدرهم واليورو، والباقة وتاريخ انتهائها.
- صلاحية مفتاح API المطلوبة: balance.
- يعيد GET /v1/balance الحقول الأربعة الأولى بنفس الصلاحية.
curl -X GET "https://api.envoisms.ma/v1/billing/balance" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"balance_mad": 1492.5,
"balance_eur": 135.68,
"primary_currency": "MAD",
"plan": "business",
"plan_expires_at": "2026-12-31 23:59:59",
"plan_days_left": 70
}قائمة المعاملات
يعيد حركات الرصيد من الأحدث إلى الأقدم: الشحن والاستهلاك والاستردادات والتسويات والأرصدة الممنوحة، ولكل منها الرصيد بعدها.
- صلاحية مفتاح API المطلوبة: billing.
curl -X GET "https://api.envoisms.ma/v1/billing/transactions?limit=50" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "tx_6e8a0c2e4b6d8f0a2c4e6b8d",
"type": "usage",
"status": "completed",
"amount_eur": -0.072,
"amount_mad": -0.792,
"balance_after_eur": 135.68,
"balance_after_mad": 1492.5,
"payment_method": null,
"description": "SMS +212612345678",
"reference": "msg_3f9a1c7e5b2d4a6f8e0c1b3d",
"stripe_invoice_id": null,
"created_at": "2026-10-22 09:14:03"
}
],
"limit": 50,
"offset": 0,
"total": 1,
"type": null
}حالة المدفوعات الآلية
يبيّن ما إذا كان بإمكان وكيل بلا حساب أن يدفع مقابل الوصول إلى API بإيداع USDC (HTTP 402، بروتوكول MPP)، وبأي شروط. بلا مصادقة.
- عند التعطيل تكون الاستجابة {"enabled": false}.
- المسار: استدعِ /v1/messages أو /v1/waba أو /v1/verify أو /v1/mcp بلا ترويسة Authorization ومع X-Payment-Method: mpp (أو Accept: application/problem+json)؛ فتكون الاستجابة 402 مع ترويسة WWW-Authenticate: Payment تصف الإيداع. أودِع deposit_usd بعملة USDC ثم أعد الاستدعاء مع Authorization: Payment <credential>. تحمل الاستجابة مفتاح API الممنوح في X-Machine-Session-Key (مرة واحدة) والإيصال في Payment-Receipt؛ ثم استعمل هذا المفتاح كمفتاح Bearer.
- بدون X-Payment-Method أو Accept: application/problem+json يتلقى الاستدعاء غير المصادَق 401 UNAUTHORIZED. الإيداع غير المؤكد بعد يعطي 402 PAYMENT_NOT_SETTLED؛ وبيانات الاعتماد غير المقروءة 400 INVALID_CREDENTIAL. لا يحمل المفتاح الممنوح إلا الصلاحيات send وstatus وbalance وverify. وبحد أقصى 5 تحديات في الدقيقة لكل عنوان IP.
curl -X GET "https://api.envoisms.ma/v1/machine-payments/status" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"enabled": true,
"deposit_usd": 5,
"credit_mad": 46.5,
"networks": ["base", "tempo"],
"permissions": ["send", "status", "balance", "verify"]
}إحصائيات الإرسال
يعيد خلال نافذة من الأيام الأحجام والتسليم المؤكد والإنفاق المخصوم وتوزيعها حسب القناة والمشغّل والحالة واليوم.
| المعلمة | النوع | الحالة | الوصف |
|---|---|---|---|
| days | integer | اختياري | حجم النافذة، 30 افتراضياً و365 كحد أقصى. |
| campaign_id | string | اختياري | إحصائيات حملة واحدة (وتُسقط الاستجابة حينها error_diagnostics وwaba_breakdown). |
- صلاحية مفتاح API المطلوبة: analytics.
- يُحسب delivery_rate على الرسائل التي أُكِّد تسليمها أو نُفي فقط؛ ويبيّن coverage_pct نسبة الرسائل التي حصلت على هذا التأكيد. القيمة المجهولة null وليست 100 أبداً. وcost_* هو ما خُصم بعد طرح الاستردادات. تُخزَّن الاستجابة مؤقتاً 60 ثانية (الترويسة X-Cache).
curl -X GET "https://api.envoisms.ma/v1/analytics" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"window_days": 30,
"campaign_id": null,
"summary": {
"total": 420,
"delivered": 398,
"failed": 6,
"unconfirmed": 16,
"delivery_rate": 98.51,
"coverage_pct": 96.19,
"cost_eur": 30.24,
"cost_mad": 332.64,
"avg_latency": 4.2
},
"channels": [
{ "channel": "sms", "total": 420, "cost_eur": 30.24, "cost_mad": 332.64, "fallback_sends": 0 }
],
"operators": [
{ "operator": "Maroc Telecom", "total": 250, "delivered": 240, "failed": 2, "unconfirmed": 8, "cost_eur": 18, "cost_mad": 198, "delivery_rate": 99.17, "coverage_pct": 96.8, "avg_latency": null }
],
"status_distribution": [
{ "status": "delivered", "total": 398 },
{ "status": "unconfirmed", "total": 16 },
{ "status": "failed", "total": 6 }
],
"daily": [
{ "day": "2026-10-21", "total": 14, "sms_total": 14, "whatsapp_total": 0, "delivered": 13, "failed": 0, "unconfirmed": 1, "cost_eur": 1.008, "cost_mad": 11.088 }
],
"error_diagnostics": [],
"waba_breakdown": []
}قائمة مفاتيح API
يعيد مفاتيح API في الحساب، النشطة والملغاة، دون قيمتها الكاملة أبداً.
- صلاحية مفتاح API المطلوبة: api_keys.
- permissions وip_whitelist سلسلتا JSON (وip_whitelist تكون null بلا تقييد).
curl -X GET "https://api.envoisms.ma/v1/api-keys" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"data": [
{
"id": "key_e84c2a6b8d0f1e3c5a7b9d2f",
"name": "Serveur de production",
"key_prefix": "smr_a8f7d6c5",
"ip_whitelist": "[\"196.200.1.4\"]",
"rate_limit": 180,
"permissions": "[\"send\",\"verify\",\"status\",\"balance\"]",
"active": 1,
"last_used_at": "2026-10-22 09:14:03",
"created_at": "2026-10-01 08:00:00",
"sandbox": false
}
]
}إنشاء مفتاح API
ينشئ مفتاحاً حقيقياً (smr_…) أو تجريبياً (env_test_…). يعيد 201؛ ولا تُعرض القيمة الكاملة (api_key) إلا مرة واحدة.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| name | string | اختياري | تسمية المفتاح (الافتراضي: "API key"). |
| permissions | string[] | اختياري | من بين send وverify وstatus وbalance وcontacts وcampaigns وwebhooks وbilling وanalytics وapi_keys وoptouts وsender_id. الافتراضي: send وverify وstatus وbalance. تُتجاهل القيم غير المعروفة. |
| ip_whitelist | string[] | اختياري | عناوين IP الوحيدة المسموح لها باستعمال المفتاح. |
| rate_limit | integer | اختياري | عدد الطلبات في الدقيقة من 10 إلى 2000. الافتراضي حسب الباقة: Starter 60، Business 180، Pro 500، Enterprise 1200. |
| sandbox | boolean | اختياري | القيمة true تنشئ مفتاح اختبار: تُتحقَّق الطلبات وتُسجَّل دون إرسال أو فوترة. ونمط المفتاح نهائي. |
- صلاحية مفتاح API المطلوبة: api_keys.
- يتوقف عدد المفاتيح الحقيقية النشطة على الباقة، وللمفاتيح التجريبية سقف خاص بها؛ وما زاد 403 LIMIT_EXCEEDED. ويضيف المفتاح التجريبي "note" إلى الاستجابة.
curl -X POST "https://api.envoisms.ma/v1/api-keys" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "OTP backend",
"permissions": [
"verify",
"status"
],
"sandbox": true
}'{
"id": "key_e84c2a6b8d0f1e3c5a7b9d2f",
"name": "Serveur de production",
"key_prefix": "smr_a8f7d6c5",
"api_key": "smr_a8f7d6c5…",
"permissions": ["send", "verify", "status", "balance"],
"rate_limit": 180,
"sandbox": false,
"warning": "The full API key is shown once. Store it securely."
}تعديل مفتاح API
يعدّل اسم المفتاح أو صلاحياته أو عناوين IP المسموحة أو حد الطلبات أو حالة تفعيله.
| الحقل | النوع | الحالة | الوصف |
|---|---|---|---|
| name | permissions | ip_whitelist | mixed | اختياري | نفس قواعد الإنشاء. |
| rate_limit | integer | اختياري | عدد الطلبات في الدقيقة من 10 إلى 1000. |
| active | boolean | اختياري | القيمة false تعلّق المفتاح. |
- صلاحية مفتاح API المطلوبة: api_keys.
- لا يتغير نمط المفتاح التجريبي أو الحقيقي: 400 SANDBOX_IMMUTABLE. أنشئ مفتاحاً جديداً.
curl -X PATCH "https://api.envoisms.ma/v1/api-keys/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "OTP backend (rotated)"
}'{
"updated": true,
"id": "key_e84c2a6b8d0f1e3c5a7b9d2f"
}إلغاء مفتاح API
يلغي مفتاحاً نهائياً: لم يعد يصادق أي طلب.
- صلاحية مفتاح API المطلوبة: api_keys.
curl -X DELETE "https://api.envoisms.ma/v1/api-keys/{id}" \
-H "Authorization: Bearer $ENVOISMS_API_KEY"{
"revoked": true,
"id": "key_e84c2a6b8d0f1e3c5a7b9d2f"
}