الرسائل
اقرأ الرسائل الواردة التي تستقبلها أرقامك، وأرسل رسائلك أنت. ردّ على محادثة بدأها أحد جهات الاتصال، أو راسِل رقم هاتف مباشرة فتفتح Sendveo المحادثة نيابة عنك.
كائن الرسالة
{
"id": "msg_7Qk2",
"channelId": "chn_sales01",
"direction": "INBOUND",
"chatId": "chat_aa11",
"from": "+212670111222",
"to": null,
"senderName": "Khadija Alaoui",
"text": "Here is the model we agreed on.",
"type": "image",
"status": "RECEIVED",
"statusAt": null,
"failureReason": null,
"timestamp": "2026-09-09T14:02:11Z",
"attachments": [
{
"id": "att_5Hq1",
"kind": "IMAGE",
"mimeType": "image/jpeg",
"fileName": "modele.jpg",
"size": 184320,
"durationSeconds": null,
"width": 1080,
"height": 1440,
"voiceNote": false,
"url": "https://api.sendveo.com/v1/messages/msg_7Qk2/attachments/att_5Hq1"
}
],
"replyTo": { "id": "msg_7Qk1", "providerMessageId": "wamid.HB1" },
"location": null,
"contacts": null,
"linkPreview": null,
"reactions": [{ "emoji": "👍", "by": "+212670111222", "at": "2026-09-09T14:03:00Z" }],
"deletedAt": null,
"editedAt": null
}يكون direction إما INBOUND أو OUTBOUND. ويحمل from المُرسِل في الرسائل الواردة ويكون null في الصادرة، بينما to صورته المعاكسة: يحمل رقم المستلِم في رسالة أرسلتها إلى رقم هاتف، ويكون null في ما عدا ذلك. أما status فيكون SENT أو FAILED في الرسائل الصادرة، والرسائل الواردة دائمًا RECEIVED.
إرسال رسالة
كل عملية إرسال تحدّد الرقم المُرسِل في channelId (ويُقبل numberId بوصفه الحقل نفسه)، ونص الرسالة في text، ومستلِمًا واحدًا فقط: chatId للردّ على محادثة قائمة، أو to لرقم هاتف. ويجب أن يكون الرقم CONNECTED، وإلا أعاد الطلب 409.
curl -X POST https://api.sendveo.com/v1/messages \
-H "X-API-KEY: sv_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "channelId": "chn_sales01", "chatId": "chat_aa11", "text": "Yes - shipping tomorrow." }'{
"message": {
"id": "msg_8Rm5",
"channelId": "chn_sales01",
"direction": "OUTBOUND",
"chatId": "chat_aa11",
"to": null,
"text": "Yes - shipping tomorrow.",
"status": "SENT",
"timestamp": "2026-09-09T14:05:40Z"
}
}الإرسال إلى رقم هاتف
استخدم to مع رقم هاتف بصيغة E.164 لمراسلة شخص لم يكتب إليك من قبل: تفتح Sendveo المحادثة وتعيد chatId الخاص بها، فتصل الردود في الخيط نفسه. وتُقبل الصيغ +212661234567 و212661234567 و00212661234567 جميعًا، بمسافات أو شرطات أو أقواس أو بدونها.
curl -X POST https://api.sendveo.com/v1/messages \
-H "X-API-KEY: sv_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "channelId": "chn_sales01", "to": "+212661234567", "text": "Your order is ready." }'{
"message": {
"id": "msg_9Tn7",
"channelId": "chn_sales01",
"direction": "OUTBOUND",
"chatId": "chat_9Fp2",
"to": "+212661234567",
"text": "Your order is ready.",
"status": "SENT",
"timestamp": "2026-09-15T09:12:03Z"
}
}الإرسال إلى رقم له محادثة قائمة يواصل تلك المحادثة بدل فتح محادثة ثانية، سواء كتب جهة الاتصال أولًا أو كتبت أنت. فلن يتوزّع الشخص الواحد أبدًا على خيطين.
إذا كان to رقم هاتف غير صالح للاستخدام، أو أُرسل chatId وto معًا، أو لم يُرسَل أيٌّ منهما، فالنتيجة في الحالات الثلاث 400 VALIDATION_FAILED مع ذكر الحقل في details، ولا يُرسَل شيء. وتُرفض الصيغة المحلية التي تبدأ بصفر مثل 0661234567: فبلا رمز دولة تبقى مجرد تخمين.
تعامل مع المراسلة الأولى باعتدال: يحظر واتساب الأرقام التي تراسل كثيرين لم يبدؤوا الحديث، والخط المحظور هو خط عملك أنت. تسمح Sendveo بـ 20 محادثة جديدة لكل رقم في الساعة، وبعدها تردّ بـ 429 OUTBOUND_LIMIT مع Retry-After. أما الردود فلا تُحتسب أبدًا.
المرفقات
يمكن أن تحمل الرسالة الواحدة حتى 10 ملفات. أرسل البايتات بصيغة multipart/form-data، أو مرّر url عامًا داخل JSON فتجلب Sendveo الملف نيابة عنك. ولا يمكن الخلط بين الملفات والروابط في رسالة واحدة. والحقل text اختياري متى حملت الرسالة ملفًا: فالصورة بلا تعليق رسالة كاملة.
في طلب multipart تُرسَل الملفات في الحقل attachments (كرّر الحقل لإرسال عدة ملفات)، أما الملف الصوتي الذي تريد عرضه كموجة صوتية داخل الرسالة فيُرسَل في voice_note. وأي اسم حقل ملف آخر يعطي 400.
curl -X POST https://api.sendveo.com/v1/messages \
-H "X-API-KEY: sv_live_your_key_here" \
-F channelId=chn_sales01 \
-F to=+212661234567 \
-F "text=Voici le devis" \
-F "attachments=@devis.pdf;type=application/pdf"{
"channelId": "chn_sales01",
"chatId": "chat_aa11",
"attachments": [
{
"url": "https://cdn.example.com/devis.pdf",
"mimeType": "application/pdf",
"filename": "devis.pdf"
}
]
}الحدود
هذه حدود واتساب نفسه، لا حدودنا: فالملف الذي يتجاوزها يرفضه واتساب بعد الرفع، ولذلك ترفضه Sendveo عند الحافة وتسمّي الحدّ. ويُرفض أي نوع خارج القائمة بالاسم. وخطأ المرفق هو 400 VALIDATION_FAILED مع details[0].path = "attachments.N.…"، فيعرف المستدعي الذي أرسل خمسة ملفات أيّها رُفض، ولا يصل شيء إلى خدمة الرسائل.
| النوع | أنواع الوسائط | الحد الأقصى |
|---|---|---|
| image | image/jpeg image/png image/gif image/webp | 5 MB |
| video | video/mp4 video/3gpp video/quicktime | 16 MB |
| audio | audio/mpeg audio/mp4 audio/aac audio/amr audio/ogg audio/wav | 16 MB |
| voice note | as audio, plus audio/opus audio/webm | 16 MB |
| document | application/pdf, the Office types, application/zip, application/json, text/plain, text/csv, text/vcard | 100 MB |
| sticker | image/webp | 500 KB |
تنزيل مرفق
تُعيد نقطة النهاية البايتات نفسها، بالمصادقة ذاتها المستعملة في بقية الـ API. وهي وسيط ناقل، لا إعادة توجيه أبدًا. ولا تخزّن Sendveo أي بايت: يُجلب الملف عند الطلب، ويُبَثّ، ثم يُنسى، فكل تنزيل استدعاء جديد، والمرفق قابل للتنزيل ما دامت خدمة الرسائل تحتفظ به. أما الملف الذي أرسلته أنت فلا يمكن تنزيله مجددًا (409): بياناته الوصفية موجودة على الرسالة لتتمكّن واجهتك من عرض ما أرسلت.
الردود والتفاعلات
اقتبس رسالة عبر replyTo: إمّا معرّف Sendveo من صيغة msg_…، وإمّا معرّف المزوّد لرسالة أقدم من تاريخ الربط، وهو مقبول ويُمرَّر كما هو. وفي الحالة الثانية تعود الرسالة حاملةً replyTo بقيمة id: null.
التفاعل هو رمز تعبيري أصلي واحد. والنص العادي يعطي 400. وإعادة التفاعل تستبدل تفاعلك ولا تضيف تفاعلًا ثانيًا أبدًا، بينما { "emoji": null } (أو DELETE …/reactions، وهي العملية نفسها) تسحبه. والحقل reactions على الرسالة يحمل التفاعلات القائمة في هذه اللحظة، لا سجلًا لها، ويظهر تفاعل الرقم المتصل نفسه بقيمة by: null.
curl -X POST https://api.sendveo.com/v1/messages/msg_8Rm5/reactions \
-H "X-API-KEY: sv_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "emoji": "👍" }'إعادة التوجيه والحذف
تردّ إعادة التوجيه بـ 201 مع النسخة المحفوظة في المحادثة الوجهة، فيُظهر صندوق الوارد لديك ما أرسلته. ويُعيد { to: "+212661234567" } التوجيه داخل المحادثة القائمة مع ذلك الرقم، أما إعادة التوجيه إلى رقم لا توجد معه أي محادثة فترد بـ 409 ولا تفتح محادثة جديدة: فتحويل إعادة توجيه إلى اتصال أول بارد ينفق سمعة الرقم دون أن تطلب ذلك.
curl -X POST https://api.sendveo.com/v1/messages/msg_8Rm5/forward \
-H "X-API-KEY: sv_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "chatId": "chat_aa11" }'الحذف يسحب الرسالة لدى واتساب. أما في Sendveo فيبقى السجل: يُفرَّغ text، ويصبح attachments بقيمة []، ويُضبط deletedAt، فيظل السجل يبيّن أن شيئًا قيل ثم سُحب. وحذف رسالة محذوفة أصلًا يعطي 409.
يتطلّب التفاعل وإعادة التوجيه والحذف لدى الجميع والتعليم كمقروء النطاق messages:manage، وهو عمدًا ليس جزءًا من messages:send: فالتصرّف في رسالة قائمة صلاحية مختلفة عن كتابة رسالة جديدة. والمفاتيح التي صدرت قبل وجود هذا النطاق لا تحمله وتردّ 403، فأصدِر مفتاحًا جديدًا بدل تعديل القديم. أما POST /v1/chats/:chatId/typing فلا يحتاج سوى messages:send.
المواقع وجهات الاتصال
يُخزَّن الموقع بشكل بنيوي ويُعاد كأرقام في الاتجاهين. والقيمة خارج المدى تعطي 400 مع تسمية location.latitude أو location.longitude. ويبقى نص text الخاص بك فوقه.
يُطبَّع كل هاتف في بطاقة جهة الاتصال بالقاعدة نفسها المطبَّقة على المستلِم، وتُرفض الصيغة المحلية التي تبدأ بصفر. وتُحلَّل بطاقات جهات الاتصال الواردة فتعود إليك أسماءً وأرقامًا بصيغة E.164. ولا يمكن أن يجتمع موقع وجهات اتصال في رسالة واحدة.
{
"channelId": "chn_sales01",
"chatId": "chat_aa11",
"text": "On est ici",
"location": {
"latitude": 33.5731,
"longitude": -7.5898,
"name": "Casablanca",
"address": "Boulevard Zerktouni"
}
}
{
"channelId": "chn_sales01",
"chatId": "chat_aa11",
"contacts": [
{ "name": "Nadia Benali", "phones": ["+212661778899"], "organisation": "Atlas" }
]
}معاينات الروابط
قيمة linkPreview الافتراضية هي true: فيعود أول رابط http(s) في النص على هيئة linkPreview: { url }، فلا تحتاج إلى إعادة تحليل نص الرسالة. ولا تجلب Sendveo الرابط أبدًا: لا عنوان، ولا صورة، ولا أي طلب خارجي مع كل إرسال.
ما لا تفعله هذه الخاصية: بطاقة المعاينة التي يراها المستلِم يرسمها تطبيق واتساب على هاتفه هو انطلاقًا من الرابط الموجود في النص، ولا توجد أي وسيلة لمنعها. وlinkPreview: false يمنع Sendveo فقط من استخراج الرابط، ولا يلغي بطاقة واتساب، ولا يُعاد كتابة النص أبدًا لمحاولة ذلك.
التعليم كمقروء ومؤشّر الكتابة
تعليم محادثة كمقروءة وإظهار مؤشّر الكتابة، كلٌّ منهما استدعاء واحد. ومؤشّر الكتابة أفضل جهد ممكن بحسب العقد: فهو يردّ 200 { "sent": false } حين ترفضه خدمة الرسائل بدل أن يُفشل طلبك، إذ لا ينبغي لمجاملة أن تُفشل مستدعيًا. ويبقى الخطأ خطأك أنت: محادثة غير معروفة (404)، أو رقم غير متصل (409)، أو مدة خارج المجال 1-25000 مللي ثانية (400).
حالة التسليم
يمرّ status على الرسالة الصادرة بالترتيب SENT ثم DELIVERED ثم READ، ولا يتقدّم إلا إلى الأمام: فالإشعارات تصل غير مرتّبة، ولا يعيد DELIVERED متأخّر رسالةً مقروءة إلى غير مقروءة. وFAILED يغلب كل شيء ولا يغلبه شيء. ويدلّ statusAt على وقت آخر تغيّر، ولا يُضبط failureReason إلا مع FAILED.
عرض الرسائل
صفِّ حسب الرقم والاتجاه والوقت. النتائج مرتّبة من الأحدث إلى الأقدم ومحدودة بـ 200 لكل صفحة.
curl "https://api.sendveo.com/v1/messages?channelId=chn_sales01&direction=INBOUND&limit=50" \
-H "X-API-KEY: sv_live_your_key_here"