واجهة REST وwebhooks لواتساب مصممة للتكاملات
أرسل القوالب والرسائل، وزامن جهات الاتصال والوسوم والمحادثات، واحصل على webhook موقَّع لكل رد وإشعار تسليم. واجهة REST بسيطة على /api/v1 بمفاتيح API محددة الصلاحيات، على منصة WhatsApp Business الرسمية. لا حاجة لتثبيت أي SDK. الواجهة البرمجية متاحة في كل الباقات، بدءاً من Developer Free مع 100 رسالة عبر الواجهة شهرياً.
✦ تجربة مجانية لمدة 14 يوماً · رقم واتساب للأعمال الخاص بك · تُصدر Meta فواتير الرسائل مباشرةً
واجهة REST واحدة لكامل CRM واتساب لديك
صادق باستخدام مفتاح API لمساحة العمل (Authorization: Bearer sm_live_…) واستدعِ /api/v1. أرسل رسائل القوالب والنصوص والوسائط والرسائل التفاعلية؛ وأنشئ جهات الاتصال وحدّثها ووسمها؛ واقرأ الوسوم ومراحل مسار المبيعات والقوالب والحملات؛ واعرض المحادثات وعيّنها وأغلقها؛ وأدِر نقاط نهاية webhook؛ وأبلغ عن المبيعات كتحويلات. كل استجابة ناجحة تستخدم الغلاف نفسه {data, meta}، وكل خطأ يحمل رمزًا ثابتًا يمكنك البناء عليه.
- تُنشأ المفاتيح من المطورون ← مفاتيح API وتُعرض مرة واحدة فقط
- 10 نطاقات صلاحيات، مع إعدادات جاهزة: وصول كامل، إرسال فقط، قراءة فقط، ومخصص
- تفوتر Meta رسوم الرسائل مباشرةً على حساب واتساب للأعمال الخاص بك؛ ولا تفرض الواجهة أي رسوم لكل رسالة
Webhooks موقَّعة يمكنك الوثوق بها
اشترك بنقطة نهاية HTTPS في أي من 12 حدثًا: الرسائل المستلمة والمرسلة والمسلَّمة والمقروءة والفاشلة؛ وجهات الاتصال المُنشأة والمُلغى اشتراكها والمُعاد اشتراكها؛ والمحادثات المعيَّنة والمغلقة؛ وتغييرات حالة القوالب؛ والحملات المكتملة. كل إرسال موقَّع بـ HMAC-SHA256 في ترويسة X-SM-Signature، ويُعاد المحاولة بعد 1د و5د و30د و2س و6س و12س، ويظهر في سجل التسليم حيث يمكنك إعادة إرساله.
- يُسلَّم مرة واحدة على الأقل: أزل التكرار باستخدام معرّف الحدث
- تُعطَّل نقاط النهاية التي تفشل باستمرار (50 مرة متتالية أو لمدة 24 ساعة) ويُنبَّه إليها
- بدّل سر التوقيع مع فترة تداخل مدتها 24 ساعة
التفاصيل التي تعتمد عليها التكاملات
-
إرسال دون تكرار
أرسل Idempotency-Key مع طلب POST. المفتاح نفسه والمحتوى نفسه خلال 24 ساعة يعيدان الاستجابة الأصلية مع Idempotent-Replayed: true، فلا تُرسل إعادة المحاولة الرسالة مرتين.
-
ترقيم بالمؤشر ومزامنة
تقبل القوائم limit (من 1 إلى 100) وcursor وتعيد nextCursor. وفي جهات الاتصال والمحادثات، يعيد updatedSince ما تغيّر فقط، الأقدم أولًا، للمزامنة التدريجية.
-
مراجعك الخاصة
أضف clientReference مثل رقم طلبك إلى عملية الإرسال. يعود في الاستجابة وفي GET /messages/{id} وفي كل webhook للإرسال والتسليم والقراءة والفشل.
-
مفاتيح بأقل صلاحيات لازمة
امنح كل تكامل مفتاحه الخاص بالصلاحيات التي يحتاجها فقط. ولا يستقبل webhook أُنشئ بمفتاح ما عائلةَ أحداث إلا ما دام ذلك المفتاح يملك صلاحية القراءة الخاصة بها.
-
سجلات الطلبات والتسليم
اطّلع في لوحة التحكم على آخر 500 طلب API خلال آخر 7 أيام، مع التصفية حسب المفتاح أو الأخطاء فقط، وأعد إرسال أي webhook من سجل التسليم الخاص بنقطة النهاية.
-
مرجع OpenAPI
مستند OpenAPI بصيغة JSON على /api/v1/openapi.json وواجهة Swagger UI على /api/v1/docs، مع ذكر صلاحية كل عملية في x-scope. والمرجع نفسه متاح في لوحة التحكم ضمن قسم المطورين.
ما تغطيه الواجهة
جميع المسارات تحت /api/v1. إنشاء القوالب والحملات متاح حاليًا من لوحة التحكم فقط: تقرؤها الواجهة وترسل القوالب المعتمدة رسالةً واحدة في كل مرة.
| المورد | نقاط النهاية | الصلاحية | ما الذي تفعله |
|---|---|---|---|
| الحساب | GET /me, GET /phone-numbers | أي مفتاح صالح | مساحة العمل والخطة وصلاحيات المفتاح والحدود والاستخدام الحالي؛ وأرقامك مع تقييم الجودة وحد المراسلة |
| الرسائل | POST /messages, GET /messages/{id} | messages:send, messages:read | رسائل القوالب (بما فيها الدوّارة والعروض محدودة المدة) والنصوص والصور والفيديو والصوت والمستندات والرسائل التفاعلية (التفاعلية تتطلب Team أو Business)؛ الحالة وسبب الفشل وفئة التسعير لدى Meta |
| جهات الاتصال | GET/POST /contacts, GET/PATCH/DELETE /contacts/{idOrPhone} | contacts:read, contacts:write | إنشاء أو تحديث حسب رقم الهاتف، والوسوم، والسمات المخصصة، والاشتراك وإلغاؤه؛ وضع جهة اتصال في مرحلة من مسار المبيعات يتطلب Business |
| الوسوم ومسار المبيعات | GET /tags, GET /pipeline-stages | contacts:read | الوسوم مع عدد جهات الاتصال؛ ومراحل مسار العملاء المحتملين |
| القوالب | GET /templates, GET /templates/{id} | templates:read | للقراءة فقط: تُنشأ القوالب في لوحة التحكم وتراجعها Meta |
| المحادثات | GET /conversations, GET /conversations/{id}, GET /conversations/{id}/messages, PATCH /conversations/{id} | conversations:read, conversations:write | سجل الرسائل وwindowOpen؛ التعيين وإلغاء التعيين والإغلاق وإعادة الفتح (القراءة متاحة في كل الباقات، والتعديلات تتطلب Team أو Business) |
| الحملات | GET /campaigns, GET /campaigns/{id} | campaigns:read | إحصاءات للقراءة فقط؛ تُنشأ الحملات وتُطلق من لوحة التحكم |
| التحويلات | POST /conversions | conversions:write | أبلغ عن عملية بيع لتُنسب إلى الحملة التي جلبت العميل (Business) |
| Webhooks | GET/POST /webhooks, DELETE /webhooks/{id} | webhooks:manage | الاشتراك بنقاط النهاية وإلغاؤه (REST hooks) |
حدود الواجهة البرمجية حسب الخطة
تُحتسب لكل مساحة عمل وليس لكل مفتاح. تحمل الاستجابات المحتسبة ترويسات RateLimit-*، وكل استجابة 429 تحمل Retry-After. ولا تُحتسب ضمن رسائل الواجهة الشهرية إلا الرسائل المرسلة عبر الواجهة البرمجية، وبعد تجاوز الحصة يتلقى الإرسال 402 PLAN_MESSAGE_LIMIT_REACHED. ويظل حد المراسلة الخاص بـ Meta لرقمك ساريًا.
| الخطة | الطلبات / دقيقة | رسائل الواجهة / ساعة | رسائل الواجهة / شهر | نقاط نهاية webhook |
|---|---|---|---|---|
| التجربة المجانية (14 يومًا) | 300 | 100 | 1,000 | 5 |
| Developer Free | 60 | 2,000 | 100 | 2 |
| Developer 1K | 120 | 10,000 | 1,000 | 2 |
| Developer 10K | 300 | 10,000 | 10,000 | 2 |
| Developer 100K | 1,200 | 10,000 | 100,000 | 2 |
| Team | 300 | 10,000 | غير محدود (استخدام عادل) | 1 |
| Business | 1,200 | 10,000 | غير محدود (استخدام عادل) | 5 |
الواجهة البرمجية فقط؟ اختر حجم Developer
باقات Developer هي الواجهة البرمجية مع لوحة تحكم للعرض فقط: مقعد واحد، ونقطتا نهاية webhook، ورسائل الواجهة الشهرية المبيّنة هنا. الأسعار شاملة ضريبة GST، والسنة بسعر عشرة أشهر. ولا توقف الأحجام المدفوعة قوالب المصادقة أبداً.
| الخطة | السعر / شهر | رسائل الواجهة / شهر | الطلبات / دقيقة |
|---|---|---|---|
| Developer Free | ₹0 | 100 | 60 |
| Developer 1K | ₹100 | 1,000 | 120 |
| Developer 10K | ₹499 | 10,000 | 300 |
| Developer 100K | ₹999 | 100,000 | 1,200 |
تحقّق من مفتاحك ثم أرسل قالبًا
لا يحتاج GET /me إلى أي صلاحية. ويحتاج الإرسال إلى messages:send وقالب معتمد.
# 1. Check your key: workspace, plan, scopes, limits and usage
curl https://waba-api.startmessaging.com/api/v1/me \
-H "Authorization: Bearer sm_live_your_api_key"
# 2. Send an approved template, safe to retry with the same Idempotency-Key
curl -X POST https://waba-api.startmessaging.com/api/v1/messages \
-H "Authorization: Bearer sm_live_your_api_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-10421-ready" \
-d '{
"to": "+919812345678",
"type": "template",
"template": {
"name": "order_ready_for_pickup",
"language": "en_US",
"parameters": [
{ "text": "Asha" },
{ "text": "10421" }
]
},
"clientReference": "order-10421"
}' const API = 'https://waba-api.startmessaging.com/api/v1';
const auth = { Authorization: 'Bearer sm_live_your_api_key' };
// 1. Check your key
const me = await fetch(`${API}/me`, { headers: auth }).then((r) => r.json());
console.log(me.data.plan.code, me.data.apiKey.scopes, me.data.limits);
// 2. Send an approved template, safe to retry with the same Idempotency-Key
const res = await fetch(`${API}/messages`, {
method: 'POST',
headers: {
...auth,
'Content-Type': 'application/json',
'Idempotency-Key': 'order-10421-ready',
},
body: JSON.stringify({
"to": "+919812345678",
"type": "template",
"template": {
"name": "order_ready_for_pickup",
"language": "en_US",
"parameters": [
{ "text": "Asha" },
{ "text": "10421" }
]
},
"clientReference": "order-10421"
}),
});
const { data } = await res.json();
console.log(data.id, data.status, res.headers.get('Idempotent-Replayed')); import requests
API = 'https://waba-api.startmessaging.com/api/v1'
AUTH = {'Authorization': 'Bearer sm_live_your_api_key'}
# 1. Check your key
me = requests.get(f'{API}/me', headers=AUTH).json()['data']
print(me['plan']['code'], me['apiKey']['scopes'], me['limits'])
# 2. Send an approved template, safe to retry with the same Idempotency-Key
res = requests.post(
f'{API}/messages',
headers={**AUTH, 'Idempotency-Key': 'order-10421-ready'},
json={
"to": "+919812345678",
"type": "template",
"template": {
"name": "order_ready_for_pickup",
"language": "en_US",
"parameters": [
{ "text": "Asha" },
{ "text": "10421" }
]
},
"clientReference": "order-10421"
},
)
data = res.json()['data']
print(data['id'], data['status']) <?php
$api = 'https://waba-api.startmessaging.com/api/v1';
$auth = 'Authorization: Bearer sm_live_your_api_key';
// 1. Check your key
$ch = curl_init("$api/me");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [$auth],
]);
$me = json_decode(curl_exec($ch), true)['data'];
echo $me['plan']['code'] . ' ' . implode(',', $me['apiKey']['scopes']) . "\n";
// 2. Send an approved template, safe to retry with the same Idempotency-Key
$body = <<<'JSON'
{
"to": "+919812345678",
"type": "template",
"template": {
"name": "order_ready_for_pickup",
"language": "en_US",
"parameters": [
{ "text": "Asha" },
{ "text": "10421" }
]
},
"clientReference": "order-10421"
}
JSON;
$ch = curl_init("$api/messages");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
$auth,
'Content-Type: application/json',
'Idempotency-Key: order-10421-ready',
],
CURLOPT_POSTFIELDS => $body,
]);
$data = json_decode(curl_exec($ch), true)['data'];
echo $data['id'] . ' ' . $data['status']; تحقّق من كل webhook قبل أن تثق به
قيمة X-SM-Signature هي v1=HMAC-SHA256(secret, timestamp + "." + المحتوى الخام). استخدم سر whsec_ كاملًا كمفتاح.
import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';
const app = express();
const SECRET = process.env.SM_WEBHOOK_SECRET; // the whole whsec_… string
function verify(rawBody, headers) {
const ts = headers['x-sm-timestamp'];
const sigs = headers['x-sm-signature'];
if (!/^\d+$/.test(ts ?? '') || typeof sigs !== 'string') return false;
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // 5-minute tolerance
const expected = createHmac('sha256', SECRET).update(`${ts}.`).update(rawBody).digest();
return sigs.split(',').some((part) => {
const m = /^v1=([0-9a-f]{64})$/.exec(part.trim());
return m !== null && timingSafeEqual(Buffer.from(m[1], 'hex'), expected);
});
}
app.post('/webhooks/startmessaging', express.raw({ type: 'application/json' }), (req, res) => {
if (!verify(req.body, req.headers)) return res.sendStatus(401);
res.sendStatus(200); // answer 2xx within 5 seconds, then do the work
const event = JSON.parse(req.body);
// Delivered at least once: skip event.id if you have already handled it.
if (event.type === 'message.delivered') {
console.log(event.data.message.clientReference, 'delivered');
}
}); import hmac, hashlib, json, os, time
from flask import Flask, request
app = Flask(__name__)
SECRET = os.environ['SM_WEBHOOK_SECRET'].encode() # the whole whsec_… string
def verify(raw_body, ts, sigs):
if not ts or not ts.isdigit() or not sigs:
return False
if abs(time.time() - int(ts)) > 300: # 5-minute tolerance
return False
expected = hmac.new(SECRET, f'{ts}.'.encode() + raw_body, hashlib.sha256).hexdigest()
return any(
hmac.compare_digest(part.strip()[3:], expected)
for part in sigs.split(',') if part.strip().startswith('v1=')
)
@app.post('/webhooks/startmessaging')
def startmessaging_webhook():
raw = request.get_data()
if not verify(raw, request.headers.get('X-SM-Timestamp'), request.headers.get('X-SM-Signature')):
return '', 401
event = json.loads(raw)
# Delivered at least once: skip event['id'] if you have already handled it.
if event['type'] == 'message.delivered':
print(event['data']['message']['clientReference'], 'delivered')
return '', 200 <?php
$secret = getenv('SM_WEBHOOK_SECRET'); // the whole whsec_… string
$raw = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_SM_TIMESTAMP'] ?? '';
$sigs = $_SERVER['HTTP_X_SM_SIGNATURE'] ?? '';
function sm_verify(string $raw, string $ts, string $sigs, string $secret): bool
{
if (!ctype_digit($ts) || $sigs === '') return false;
if (abs(time() - (int) $ts) > 300) return false; // 5-minute tolerance
$expected = hash_hmac('sha256', $ts . '.' . $raw, $secret);
foreach (explode(',', $sigs) as $part) {
$part = trim($part);
if (strncmp($part, 'v1=', 3) === 0 && hash_equals($expected, substr($part, 3))) {
return true;
}
}
return false;
}
if (!sm_verify($raw, $ts, $sigs, $secret)) {
http_response_code(401);
exit;
}
http_response_code(200); // answer 2xx within 5 seconds
$event = json_decode($raw, true);
// Delivered at least once: skip $event['id'] if you have already handled it.
if ($event['type'] === 'message.delivered') {
error_log($event['data']['message']['clientReference'] . ' delivered');
} أبلغ عن عملية بيع وانسبها إلى الحملة
في باقة Business ينسب POST /api/v1/conversions عملية البيع إلى آخر حملة سُلّمت إلى هذا الرقم ضمن نافذة التحويل لديك (7 أيام ما لم تغيّرها)، ويجمع تقرير الحملة القيمة.
# Business plan, scope conversions:write
curl -X POST https://waba-api.startmessaging.com/api/v1/conversions \
-H "Authorization: Bearer sm_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"phone": "+919812345678",
"orderId": "ORD-10442",
"value": 2499.5,
"currency": "INR",
"occurredAt": "2026-10-12T10:30:00+05:30"
}'
# 201 = recorded now, 200 = this orderId was reported already const res = await fetch('https://waba-api.startmessaging.com/api/v1/conversions', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.SM_API_KEY, // sm_live_your_api_key
'Content-Type': 'application/json',
},
body: JSON.stringify({
phone: '+919812345678',
orderId: 'ORD-10442', // the same orderId again never counts the sale twice
value: 2499.5,
currency: 'INR',
}),
});
const { data } = await res.json();
console.log(data.created, data.conversion.campaign?.name ?? 'no campaign credited'); import os, requests
res = requests.post(
'https://waba-api.startmessaging.com/api/v1/conversions',
headers={'Authorization': f"Bearer {os.environ['SM_API_KEY']}"}, # sm_live_your_api_key
json={
'phone': '+919812345678',
'orderId': 'ORD-10442',
'value': 2499.5,
'currency': 'INR',
},
)
data = res.json()['data']
campaign = data['conversion']['campaign']
print(data['created'], campaign['name'] if campaign else 'no campaign credited') واجهة واتساب البرمجية — الأسئلة الشائعة
هل توجد مكتبة SDK؟
لا. الواجهة REST بسيطة عبر HTTPS مع JSON، لذا تعمل معها أي لغة أو أداة قادرة على إرسال طلب HTTPS. يعرض التوثيق أمثلة cURL وNode.js وPython وPHP، ويصف مستند OpenAPI كل حقل في الطلبات والاستجابات.
ما الخطط التي تتضمن الواجهة البرمجية؟
كل الباقات، بما فيها Developer Free. باقات Developer هي الواجهة البرمجية مع لوحة تحكم للعرض فقط، وتختلف أحجامها بعدد رسائل الواجهة شهرياً: Developer Free مع 100 رسالة، ثم 1K و10K و100K. وتضيف باقتا Team وBusiness صندوق الوارد المشترك وجهات الاتصال والحملات، دون حد شهري لرسائل الواجهة ضمن الاستخدام العادل. وتشمل التجربة المجانية لمدة 14 يوماً الواجهة البرمجية مع 1,000 رسالة. ويعتمد عدد الطلبات في الدقيقة ونقاط نهاية webhook على الباقة، ويعرض GET /me باقتك وحدودها وعدد رسائل هذا الشهر.
هل يمكنني إنشاء القوالب أو الحملات عبر الواجهة؟
ليس بعد. تُنشأ القوالب في لوحة التحكم وتراجعها Meta، وتُبنى الحملات وتُطلق من معالج الحملات في لوحة التحكم. تقرأ الواجهة كليهما، وترسل القوالب المعتمدة رسالةً واحدة في كل مرة عبر POST /messages، وتُعلمك بانتهاء الحملة عبر webhook campaign.completed.
هل يمكن للواجهة البرمجية إرسال القوالب الدوّارة والعروض محدودة المدة؟
نعم. للقالب الدوّار أرسل template.carouselCards بإدخال لكل بطاقة وبالترتيب، ولكل منها headerMediaUrl (https) مع parameters وbuttonParameters اختيارياً. وللعرض محدود المدة عيّن template.offerExpiresAt بوقت مستقبلي بصيغة ISO 8601، فيعرض واتساب للعميل عدّاداً تنازلياً. لا يمكن للحملات إرسال هذه القوالب، لذا تُرسل واحدة تلو الأخرى عبر الواجهة البرمجية أو صندوق الوارد.
ماذا يحدث عند نفاد الحصة الشهرية لرسائل الواجهة البرمجية؟
تتضمن باقات Developer والتجربة المجانية عدداً من رسائل الواجهة شهرياً يُحتسب من اليوم الأول من الشهر، ولا تُحتسب إلا الرسائل المرسلة عبر الواجهة البرمجية. يتوقف Developer Free والتجربة عند حصتهما. أما باقة Developer المدفوعة فتوقف الرسائل الأخرى عند ثلاثة أضعاف حجمها ولا توقف قوالب المصادقة أبداً. ويتلقى الإرسال الذي يتجاوز الحد 402 PLAN_MESSAGE_LIMIT_REACHED مع sent وstopAt وresetsAt في التفاصيل، ويعرض GET /me العدد قبل حدوث ذلك. ولا يوجد في Team وBusiness حد شهري لرسائل الواجهة (استخدام عادل).
كيف أربط Zapier أو Make أو Pabbly Connect؟
لا توجد بعد تطبيقات رسمية لـ StartMessaging في هذه المتاجر. استخدم وحداتها العامة: مشغّل catch-hook أو webhook مخصص لاستقبال الأحداث، وإجراء طلب HTTP بمفتاح API الخاص بك لاستدعاء الواجهة. ومن يبني تطبيقه الخاص يمكنه الاشتراك وإلغاؤه عبر POST /webhooks وDELETE /webhooks/{id}.
ماذا يحدث إذا أعدت محاولة الإرسال؟
مع Idempotency-Key نفسه والمحتوى نفسه خلال 24 ساعة، تحصل على الاستجابة الأصلية مع Idempotent-Replayed: true، ولا يُرسل شيء مرتين. وإعادة استخدام المفتاح مع محتوى مختلف تُرفض بالرمز IDEMPOTENCY_KEY_REUSED. وبعد استجابة 429 أو 5xx، أعد المحاولة بالمفتاح نفسه.
كيف تعمل حدود المعدل؟
الحدود لكل مساحة عمل، لذا لا تزيد المفاتيح الإضافية من السعة أبداً. الطلبات في الدقيقة: 60 في Developer Free، و120 في Developer 1K، و300 في Developer 10K وTeam والتجربة المجانية، و1,200 في Developer 100K وBusiness. رسائل الواجهة في الساعة: 10,000 في الباقات المدفوعة، و2,000 في Developer Free، و100 في التجربة. يُحتسب الإرسال على الحدّين معاً، والإرسال المرفوض يعيد حصته الساعية. وتخبرك الاستجابة 429 بمدة الانتظار في Retry-After. ويُطبَّق حد المراسلة الخاص بـ Meta لرقمك فوق ذلك.
هل يفرض StartMessaging رسومًا على كل رسالة عبر الواجهة؟
لا. StartMessaging مزوّد تقني لدى Meta (Meta Tech Provider): تربط حساب واتساب للأعمال الخاص بك، وتحتسب Meta رسوم الرسائل عليه مباشرة بأسعارها. تحتسب Meta كل قالب يتم تسليمه، بما في ذلك قوالب الخدمات حتى داخل نافذة الـ24 ساعة، وردود خدمة العملاء بعد تجاوز حد مجاني شهري. ويتقاضى StartMessaging اشتراكاً ثابتاً دون محفظة ودون هامش على كل رسالة؛ وتختلف أحجام باقات Developer بعدد رسائل الواجهة الشهرية التي تتضمنها. ويُظهر الحقل pricing في كل رسالة كيف صنّفتها Meta.
ابنِ تكاملك مع واتساب
تحصل مساحة عملك الأولى على تجربة مجانية لمدة 14 يومًا، وبعدها تُبقي باقة Developer Free الواجهة البرمجية تعمل مع 100 رسالة شهريًا. اربط رقم واتساب للأعمال الخاص بك، وأنشئ مفتاحًا، ونفّذ أول استدعاء.