> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# أرسل حملة من الطرف إلى الطرف: مسودة، جمهور، dry-run، إطلاق، تحليلات

> خذ الحملة من المسودة إلى الإطلاق إلى القياس — اختر الجمهور، وعاين المستلمين، وشغّل الـ dry-run السابق للإطلاق، وأطلقها كإرسال دفعة واحدة أو كسلسلة drip، واقرأ الإحصاءات ورفع الـ holdout وعائد الاستثمار.

# أرسل حملة من الطرف إلى الطرف

يمشي هذا الدليل دورة حياة الحملة كاملة عبر الواجهة البرمجية: إنشاء مسودة، وربط جمهور، ومعاينة التخصيص، وتشغيل الـ dry-run السابق للإطلاق، والإطلاق، وقياس النتيجة. اتبعه مرة واحدة وستعمل السلسلة نفسها على كل قناة — SMS وWhatsApp والبريد الإلكتروني وRCS والصوت وpush.

يمكن لمستخدمي الـ SDK تشغيل دورة الحياة كاملة أدناه من دون cURL: كل خطوة تقترن كتلة curl فيها بتبويب [Node SDK](https://docs.orbit.devotel.io/sdks/node.md) — المساعد المُكتب `orbit.campaigns.*` حيث يوجد، وإلا المهرب العام `orbit.request(method, path, body)` الذي يغطي كل نقطة في سطح الواجهة البرمجية.

لِمخططات الطلب والاستجابة راجع [مرجع واجهة campaigns البرمجية](https://docs.orbit.devotel.io/api-reference/endpoints/campaigns.md). تغطي هذه الصفحة سير العمل؛ ويغطي [دليل اختبار A/B](https://docs.orbit.devotel.io/guides/campaign-ab-testing.md) المتغيرات، فيما يغطي [أفضل الممارسات](https://docs.orbit.devotel.io/guides/best-practices.md) و[قوائم opt-out](https://docs.orbit.devotel.io/guides/opt-out-lists.md) تفاصيل التوصيل والامتثال — تُربط أدناه بدل تكرارها.

## 1. المتطلبات المسبقة

* **مفتاح واجهة برمجية بنطاق `campaigns:write`.** تقبل كل قراءة أدناه (معاينة الجمهور، الـ dry-run، الإحصاءات) أيضًا `campaigns:read`، لكن أفعال الإطلاق تتطلب write. أنشئ المفاتيح أو حررها في لوحة التحكم تحت Settings → API keys.
* **Node SDK (اختياري).** `npm install @devotel-orbit/node` إذا كنت تفضل تبويبات الـ SDK على cURL.
* **هوية إرسال لقناتك.** لـ SMS/MMS يعني ذلك رقمًا أو مجمع مرسلين بالتسجيلات الصحيحة (يتطلب A2P في الولايات المتحدة [10DLC](https://docs.orbit.devotel.io/guides/10dlc-registration.md))؛ ولـ WhatsApp حساب WABA متصلًا؛ ولبريد الإلكتروني نطاق إرسال موثقًا.
* **مصدر جمهور معتمد.** قائمة جهات اتصال، أو شريحة حية، أو CSV بعناوين المستلمين، أو جمهور CRM خارجي (قائمة HubSpot / تقرير Salesforce)، أو مجموعة معرفات جهات اتصال منتقاة يدويًا. يجب أن يأتي الجمهور من مستلمين مشتركين — فمعالجة الـ suppression وإلغاء الاشتراك تُفرض وقت الإرسال ولا تُترك للمرسل.

## 2. أنشئ حملة المسودة

أنشئ الحملة بقناة وجمهور و`message_template`. القالب اختياري عند الإنشاء — مسودة بلا قالب لا يمكن إطلاقها — لكن توفيره منذ البداية يُبقي المسودة مكتملة.

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Retail promo — summer sale",
    "type": "blast",
    "channel": "sms",
    "audience_type": "list",
    "audience_id": "list_summerVIPs",
    "message_template": "Hi {{first_name}}, early access to our summer sale starts now: 25% off through Sunday. Reply STOP to opt out.",
    "send_time_optimization": "fixed"
  }'
```

```typescript Node.js (TypeScript) theme={null}
import { Orbit } from "@devotel-orbit/node";

const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY });

const campaign = await orbit.campaigns.create({
  name: "Retail promo — summer sale",
  channel: "sms",
  message_template:
    "Hi {{first_name}}, early access to our summer sale starts now: 25% off through Sunday. Reply STOP to opt out.",
});
```

يأخذ المساعد المُكتب ثلاثي `name` / `channel` / `message_template` الأساسي. لتمرير الجسم الكامل الموضح في تبويب curl (`type` و`audience_type` و`send_time_optimization` …) يقبل مهرب الـ SDK‏ `orbit.request("POST", "/campaigns", body)` أي حقول. يتطلب أيٌّ من الشكلين نطاق `campaigns:write` على مفتاحك.

الحقول الأساسية عند الإنشاء:

| الحقل | ملاحظات |
| - | - |
| `type` | `blast` (إرسال واحد مجدول)، أو `drip` (سلسلة متعددة الخطوات)، أو `journey` (مدفوعة بالأحداث)، أو `ad`. |
| `audience_type` | `all` أو `list` أو `segment` أو `csv` أو `manual` (معرفات جهات اتصال منتقاة يدويًا). |
| `audience_id` | مطلوب عندما يكون `audience_type` هو `list` أو `segment`. |
| `audience_source` | واصف CRM اختياري (`hubspot_list`، `salesforce_report` …) يتجاوز زوج list/segment وقت الإرسال. |
| `message_template` | حتى 100,000 محرف؛ تتسع أجسام البريد الإلكتروني بيسر. تستخدم رموز التخصيص صيغة `{{token}}`. |
| `subject` | سطر موضوع خاص بالبريد الإلكتروني. يُتجاهل في القنوات غير البريدية. |
| `send_time_optimization` | `fixed` (وقت دفعة واحد) أو `recipient-optimal` (أفضل وقت لكل جهة اتصال، انظر الخطوة 6). |
| `throttle_rate` | سقف الإرسال بالرسائل/الثانية، من 0 إلى 1000. غياب القيمة أو 0 = وتيرة المنصة الافتراضية. |
| `scheduled_at` | طابع زمني ISO للإطلاق، أو عدّله لاحقًا. يجب أن يكون في المستقبل. |
| `recurrence` | جدول اختياري للحملات المتكررة. |
| `credit_cap_usd_cents` | سقف إنفاق صارم؛ تتوقف الحملة تلقائيًا عند بلوغه. |

يعيد الاستجابة الحملة بحالة `draft`. لا يُرسل شيء حتى تطلقها الخطوة 6 صراحة.

## 3. اربط الجمهور وتحقق منه

ثلاثة أشكال للجمهور هي الشائعة؛ وتُعاين كلها بالطريقة نفسها.

**قائمة أو شريحة.** مرّر `audience_type: "list" | "segment"` مع `audience_id`، أو واصف `audience_source` من CRM. تُقيَّم الشرائح حيًّا وقت الإرسال، لذا تستمر الشريحة النامية في العمل.

**مستلمو CSV.** لقائمة تُستخدم مرة واحدة، أرسل العناوين بطلب POST مباشرة في مصفوفة `variables.csv_recipients` على الحملة (بصيغة E.164 لقنوات الهاتف). تخزنها الحملة، ويعلّم المحلل الإدخالات غير القابلة للتسليم بدل اختراع مستلمين.

**اختيارات يدوية.** `audience_type: "manual"` مع `variables.manual_contact_ids` يستهدف معرفات جهات اتصال محددة — بحساب الأهلية نفسه المستخدم مع القائمة.

قبل الإطلاق، احسب دائمًا إلى ماذا يحلّ الجمهور. يقبل `POST /api/v1/campaigns/audience/preview` شكل الجمهور نفسه الذي تحمله الحملة ويعيد `matching_count` إضافة إلى عينة من 10 جهات اتصال:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/audience/preview" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "audience_type": "list",
    "audience_id": "list_summerVIPs",
    "channel": "sms"
  }'
```

```typescript Node.js (TypeScript) theme={null}
import { Orbit } from "@devotel-orbit/node";

const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY });

// No typed helper wraps this endpoint — the SDK index
// ([/sdks](/sdks#node)) routes uncovered endpoints through
// the generic `request()` escape hatch.
const preview = await orbit.request("POST", "/campaigns/audience/preview", {
  audience_type: "list",
  audience_id: "list_summerVIPs",
  channel: "sms",
});
```

مرّر `channel` وستعيد الاستجابة أيضًا إسقاطًا صافيًا: إجمالي التطابقات ناقص صفوف قائمة الـ suppression، وناقص حالات opt-out على مستوى القناة، وناقص جهات الاتصال التي لا تملك عنوانًا قابلًا للتسليم لتلك القناة (مثل غياب البريد الإلكتروني المسجل لحملة بريدية). يعرض تقدير استرشادي `frequency_capped` عدد الإرسالات التي سيحجبها سقف التردد تقريبًا. تُخزَّن المعاينة مؤقتًا خمس دقائق لكل معيار، لذا فإن إعادة رسم الدرج نفسه رخيصة.

إذا عاد `matching_count` صفرًا، أصلح الجمهور قبل المضي — يبلغ الـ dry-run (الخطوة 5) عن الجماهير الفارغة بصوت عالٍ، لكن المعاينة هي مكان التكرار والتصحيح.

## 4. التخصيص واللغة

تستخدم القوالب عناصر نائبة بصيغة `{{token}}` تُحل لكل مستلم من حقول جهات الاتصال ومن `variables` الحملة — و`{{first_name}}` هو الحالة الشائعة. تسقط الرموز غير المحلولة إلى سلاسل فارغة وقت الإرسال، وتخبرك نقطة المعاينة أي الرموز تُحل لكل جهة اتصال، فيُلتقط خطأ إملائي مثل `{{frist_name}}` قبل الإطلاق.

لترجمة قالب محفوظ إلى لغة أخرى استخدم `POST /api/v1/campaigns/templates/translate`، الذي يعيد كتابة جسم القالب للغة المستهدفة مع الحفاظ على الرموز وتعليمات opt-out. ضمانتان يجب تذكرهما:

* تُحفظ الرموز حرفيًا عبر الترجمة.
* تبقى تعليمة opt-out الوحيدة مرة واحدة بالضبط (متطلب مشغلي الشبكات).

خزّن الجسم المترجم كقوالب متغيرات على الحملة، أو قسّم الجمهور حسب اللغة وشغّل حملة لكل لغة. وفي الحالتين يتحقق الـ dry-run (الخطوة التالية) من القالب النهائي.

## 5. ما قبل الإطلاق: شغّل الـ dry-run

قبل الإطلاق، أرسل الـ dry-run بطلب POST. إنه للقراءة فقط — لا يعدّل أي حالة، ولا يمس المحفظة، ولا يضع أي مهمة في الطابور — فاستدعِه مع كل ضغطة مفتاح في المعالج إن شئت.

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/cmp_abc123/dry-run" \
  -H "X-API-Key: $ORBIT_API_KEY"
```

```typescript Node.js (TypeScript) theme={null}
import { Orbit } from "@devotel-orbit/node";

const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY });

const report = await orbit.request(
  "POST",
  `/campaigns/${"cmp_abc123"}/dry-run`,
);
// Same escape hatch as the audience preview above — no body needed.
```

يجمع التقرير ثماني سلال:

* `audience.total_in_audience` — العدد الإجمالي للتطابقات.
* `audience.suppressed` / `opted_out` / `unreachable` — شرائح الإسقاط الصافي (قائمة الـ suppression، وحالات opt-out لكل قناة، وغياب عنوان القناة).
* `audience.held_out` — المستلمون المقتطعون في شريحة التحكم لقياس الزيادة عندما يكون `variables.campaign_holdout_percent` مضبوطًا؛ ويُستبعدون من التسليم.
* `audience.deliverable` — العدد الصافي الدقيق الذي سيحاول الإطلاق الوصول إليه.
* `cost.unit_price_cents` / `cost.total_cents` — تُسعَّر عبر المحلل نفسه الذي يستخدمه مسار الإرسال، فيتطابق الرقم مع خصم المحفظة ضمن حدود التقريب.
* `cost.balance_cents` / `cost.sufficient` — فحص المحفظة؛ الـ dry-run الأخضر يستلزم بوابة رصيد خضراء عند الإطلاق.
* `quiet_hours.skipped_estimate` و`quiet_hours.window` — نسبة التجاوز المتوقعة من ساعات الهدوء بتوقيت المستلم المحلي، إضافة إلى النافذة المحلولة قيد التقييم.
* `channel_waterfall` — القناة الأساسية وشكل التراجع بين القنوات، لتتحقق من RCS ← SMS (أو أي سلسلة ضبطتها) قبل الضغط على إرسال.
* `warnings[]` — مشكلات غير حاجبة (لا مزود مسجلًا للقناة، ساعات هدوء تغلق النافذة بالكامل …) تُعرض منفصلة عن الإخفاقات الصارمة.

بوابتان اختياريتان قبل الإطلاق:

* **الموافقات.** إذا كانت مؤسستك تتطلب توقيع مشرف، يعيد الإطلاق (`POST /campaigns/:id/send`) حالة انتظار موافقة بدل الإرسال؛ ويقع طابور الموافِقين عند `GET /campaigns/approvals/pending`، وإعدادات المؤسسة الافتراضية عند `GET/PUT /campaigns/approvals/settings`.
* **جاهزية المزود.** يفحص الـ dry-run نظافة القوائم وتسجيل المزود ضمنيًا عبر تحذيراته — قناة بلا مزود مهيأ تظهر هناك بدل أن تفشل وقت الإرسال.

الـ dry-run السليم الذي يعيد `warnings: []` و`cost.sufficient: true` يخلي سبيل الإطلاق.

## 6. أطلق واختر شكل الإرسال

أطلق بـ`POST /api/v1/campaigns/cmp_abc123/send` (أو بـ`scheduled_at` لدفعة مستقبلية). أربعة أشكال إرسال تختار منها:

**Blast.** دفعة واحدة في وقت واحد. `type: "blast"`. أبسط الأشكال — يخرج الجمهور القابل للتسليم كاملًا في مرور واحد بوتيرة `throttle_rate`.

**سلسلة Drip.** `type: "drip"` مع مصفوفة `steps` (حتى 50 خطوة). لكل خطوة قناتها وتأخيرها وحارس شرط القراءة الاختياري. الـ`message_template` على الحملة هو رسالة الترحيب للخطوة 0. يظهر تقدم الـ Drip تحت `GET /campaigns/:id/drip-analytics`.

**Journey.** `type: "journey"` — تسجيل مدفوع بالأحداث مع تحليلات لكل عقدة عند `GET /campaigns/:id/journey-analytics` وتفصيلات عقد عند `GET /campaigns/:id/journey/node-analytics`. تجري نقطة محاكاة الـ journey (`POST /campaigns/journeys/simulate`) تشغيلًا تجريبيًا للرسم البياني قبل الإطلاق كما يفعل الـ dry-run للـ blast مع الجماهير. يمشي [دليل باني رحلات الحملات](https://docs.orbit.devotel.io/guides/campaign-journey-builder.md) اللوحة المرئية وبوابات التحقق من الرسم البياني والمحاكي وربط أهداف التحويل من الطرف إلى الطرف.

**سلّم التراجع بين القنوات.** في الحملات متعددة القنوات، مصفوفة `channels` سلسلة مرتبة (بعمق أقصى مفروض وبلا قنوات مكررة). تحمل كل مدخلة غير أخيرة مُطلق `fallback_on` — `failed` أو `no_delivery` أو `no_engagement` — إضافة إلى نافذة `fallback_after_seconds`. المدخلة الأخيرة نهائية ولا تحمل حقول تراجع. وعندما توجد السلسلة يجب أن يساوي `channel` على المستوى الأعلى مدخلتها الأولى. يقارن [دليل سلاسل التراجع](https://docs.orbit.devotel.io/guides/fallback-chains.md) الموحَّد هذا السطح بسياسة `cascade_policy` لكل رسالة وسلاسل ملفات Verify، ويمشي أمثلة عملية لكليهما.

أيًّا كان الشكل، تنطبق ضوابط الوتيرة هذه:

* **Throttle.** يحد `throttle_rate` (رسائل/ثانية) من سرعة الإرسال — الافتراضي على المنصة 50 رسالة/ثانية؛ والقيمة 0 الصريحة تعني الافتراضي أيضًا.
* **الإرسال الذكي.** يجدول `send_time_optimization: "recipient-optimal"` كل مستلم في نافذة أفضل وقت له التالية من ملفات التفاعل، ويفرد التسليم على الساعات الأربع والعشرين التالية. عاينه أولًا بـ`POST /campaigns/:id/preview-smart-send` الذي يعيد أوقاتًا مقترحة لكل جهة اتصال إضافة إلى تلميح `next_best_channel` للعينة. ولقياس ما إذا كان التوقيت نفسه يؤتي ثماره، اشترك في شريحة hold-out لكل مستلم عبر `variables.smart_send_holdout_pct` — انظر الخطوة 7.
* **معاينة القناة.** إذا كانت قناة الحملة ما تزال مفتوحة، تحقق منها على عينة من المستلمين بـ`POST /messages/route-preview` — توصية الموجّه الذكي وسلسلة التراجع وكلفة كل رسالة ودرجة التفاعل، من دون إرسال شيء. لتسليح تلك السلسلة للإرسال (فيتدرج الفشل النهائي بدل التوقف عند أول قناة) مرّر `cascade_policy` في الطلب نفسه — يغطي [دليل سلاسل تراجع الإرسال الذكي](https://docs.orbit.devotel.io/guides/smart-send-fallback-chains.md) شكل السياسة لكل رسالة، ويمشي [دليل معاينة المسار الذكي](https://docs.orbit.devotel.io/guides/smart-route-preview.md) النقطة ونمط خط الإرسال.
* **نافذة الإرسال.** يؤجل `send_window: { start_local_hour, end_local_hour }` المستلمين الذين يقع وقتهم المحلي خارج النافذة — نافذة موجبة لكل مستلم، تختلف عن ساعات الهدوء (فلتر سالب).

## 7. راقب وقِس

**إحصاءات حية.** يعيد `GET /campaigns/:id/stats` مجاميع sent وdelivered وfailed وopened وclicked وreplied. ومن الـ SDK يكون ذلك `orbit.request("GET", \`/campaigns/\${id}/stats\`)`— المهرب مجددًا، إذ يتوقف المورد المُكتب عند`send`. ولتفصيل أدق تعرض حملات drip وjourney تباعًا `/drip-analytics` و`/journey-analytics` و`/journey/node-analytics\`. تتبع إعادات المحاولة سياسة إعادة المحاولة في المراسلة — فتُعاد الإخفاقات ذات الرموز القابلة لإعادة المحاولة إلى الطابور حتى تستنفد السياسة.

**معايير الخروج.** أعلن أهداف التحويل معاييرَ خروج (أو `exit_goals` في الـ drips): عندما تبلغ جهة اتصال الهدف (مثل `purchase_completed`) تغادر السلسلة بدل تلقي الباقي. تحقق من معايير الخروج وقت الحفظ؛ ويفرضها المقيّم وقت التشغيل.

**رفع الـ Holdout.** إذا ضبطت الحملة hold-out (`campaign_holdout_percent` في `variables`، أو `ab_holdout_percent` في اختبارات A/B) يقارن `GET /campaigns/:id/holdout-lift` شريحة المعالجة بالتحكم ويعيد فرق معدل التحويل مع فاصل ثقة Wald — لترى ما إذا كانت الرسالة قد تفوقت على عدم فعل شيء، لا أي متغير فاز فحسب. تعرض الـ journeys الأمر نفسه تحت `GET /campaigns/:id/journey/holdout-lift`؛ والإرسال الذكي تحت `GET /campaigns/:id/smart-send-holdout-lift`.

**Hold-out الإرسال الذكي (توقيت لكل مستلم).** يجيب الـ holdout على مستوى الحملة أعلاه عن سؤال «هل أفاد الإرسال أصلًا؟» — فلا تتلقى شريحة التحكم أي رسالة. لعزل تحسين وقت الإرسال نفسه، اضبط `variables.smart_send_holdout_pct` (من 0 إلى 50، وتُقلم القيم فوق 50) على حملة `recipient-optimal`. تحتفظ هذه الشريحة من المستلمين بالجدول الطبيعي السابق للتحسين فيما ينال الباقون أفضل وقت متوقع لهم؛ ويتلقى الفريقان الحملة، فيعزل القياس التوقيت. الإسناد حتمي لكل جهة اتصال (ثابت عبر الإيقاف/الاستئناف وإعادة الإطلاق) ويستخدم بذرة خاصة به غير مترابطة مع الـ holdout على مستوى الحملة. عدم الضبط أو 0 يعني لا hold-out. اقرأ النتيجة بـ`GET /campaigns/:id/smart-send-holdout-lift` الذي يعيد `configured` و`holdout_pct` والـ`lift` الأساسي على إشارة التحويل بالنقر، وتفصيل `metrics` يُقيَّم على delivered/read/replied لتنال القنوات بلا روابط (SMS العادي ومعظم WhatsApp) رقمًا حقيقيًا أيضًا. حملة بلا hold-out مضبوط تعيد `configured: false` مع 200 لا خطأ.

**ROI / ROAS.** يعيد `GET /campaigns/:id/roas` الإيراد المنسوب (بتفصيلي اللمسة الأخيرة واللمسات المتعددة) وقمع النقر والإيراد الزائد على الـ holdout. ويعرض `GET /campaigns/:id/roas/touchpoints` مساهمة كل لمسة. استخدم `GET /campaigns/:id/cost` و`/campaigns/:id/export` لمطابقة الإنفاق وسحب الصفوف الخام.

**قياس متكرر.** في الحملات المتكررة المجدولة تنطبق نقاط الإحصاءات نفسها على كل حدوث؛ ويجمّع `GET /campaigns/stats` عبر الحملات.

## 8. اشترك في webhooks الحملة

سجّل نقطة النهاية (راجع [نظرة عامة على webhooks](https://docs.orbit.devotel.io/webhooks/overview.md)) واشترك في أحداث `campaign.*` لتشغيل CRM خاص بك أو إنذاراتك:

* `campaign.created` / `campaign.updated` / `campaign.deleted`
* `campaign.started` / `campaign.paused` / `campaign.resumed` / `campaign.cancelled` / `campaign.completed`
* `campaign.drip_step.sent` / `campaign.drip_step.completed` / `campaign.drip.completed`

للتحقق من التواقيع وإعادات المحاولة والرسائل الميتة، يمشي [دليل مستهلك webhook المتين](https://docs.orbit.devotel.io/guides/webhook-consumer.md) النمط كاملًا.

## 9. استكشاف الأخطاء وإصلاحها

* **يحلّ الجمهور إلى صفر.** أعد تشغيل معاينة الجمهور مع تمرير `channel`؛ يخبرك الإسقاط الصافي أي شريحة أفرغته (كله suppressed، أو كله opt-out، أو لا عنوان قابلًا للوصول). أصلح القائمة أو تغطية عناوين القناة، لا الحملة.
* **يعرض الـ dry-run تحذيرات حمراء.** إدخالات `warnings[]` صريحة — «no provider registered» تعني أن القناة بلا هوية إرسال مهيأة؛ و«quiet hours fully closed» تعني أن النافذة المحلولة ترفض كل ساعة محلية؛ و«insufficient balance» تعني `cost.sufficient: false`. أزل التحذير وأعد تشغيل الـ dry-run.
* **يعيد الإطلاق 402 أو شكل انتظار موافقة.** فشل فحص الرصيد، أو أن سير عمل الموافقات مفعّل — إمّا اشحن المحفظة أو انتظر `POST /campaigns/approvals/:id/approve`.
* **حملة journey عالقة في paused.** تتوقف الحملة تلقائيًا عند بلوغ `credit_cap_usd_cents` أو عند شرط drip معطوب. افحص `GET /campaigns/:id/stats` وأزل العائق ثم `POST /campaigns/:id/resume`. تحتفظ الـ journeys المتوقفة بحالة التسجيل؛ ويستأنف الاستمرار من حيث توقفت.
* **يفرد الإرسال الذكي التسليم أكثر من اللازم.** يوزع `recipient-optimal` الإرسالات عمدًا على 24 ساعة. إذا كنت تحتاج دفعة واحدة، اضبط `send_time_optimization: "fixed"` واستخدم `send_window` إذا امتد المستلمون عبر مناطق زمنية.

## مثال عملي: promo تجزئة، من المسودة إلى التحليلات

عرض SMS صيفي لقائمة VIP، مع hold-out ليكون رقم الرفع حقيقيًا.

1. **أنشئ المسودة.**

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Retail promo — summer sale",
    "type": "blast",
    "channel": "sms",
    "audience_type": "list",
    "audience_id": "list_summerVIPs",
    "message_template": "Hi {{first_name}}, early access to our summer sale: 25% off through Sunday. Reply STOP to opt out.",
    "send_time_optimization": "recipient-optimal",
    "throttle_rate": 100,
    "variables": { "campaign_holdout_percent": 10 },
    "credit_cap_usd_cents": 50000
  }'
```

2. **عاين الجمهور.**

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/audience/preview" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "audience_type": "list", "audience_id": "list_summerVIPs", "channel": "sms" }'
```

توقع `matching_count` سليمًا، وشريحة `suppressed` + `opted_out` صغيرة، و`unreachable` يساوي صفرًا.

3. **شغّل الـ dry-run.**

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/cmp_abc123/dry-run" \
  -H "X-API-Key: $ORBIT_API_KEY"
```

تأكد من `audience.deliverable` (صافي بعد الـ hold-out بنسبة 10%) و`cost.sufficient: true` وعدم وجود تحذيرات.

4. **عاين أوقات الإرسال الذكي** (اختياري).

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/cmp_abc123/preview-smart-send" \
  -H "X-API-Key: $ORBIT_API_KEY"
```

5. **أطلق.**

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/cmp_abc123/send" \
  -H "X-API-Key: $ORBIT_API_KEY"
```

6. **اشترك في أحداث دورة الحياة** (`campaign.started` و`campaign.completed`) واقرأ الإحصاءات مع تراكم حالات التسليم.

7. **قِس.** بعد اكتمال الإرسال:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/campaigns/cmp_abc123/holdout-lift" \
  -H "X-API-Key: $ORBIT_API_KEY"
curl "https://api.orbit.devotel.io/api/v1/campaigns/cmp_abc123/roas" \
  -H "X-API-Key: $ORBIT_API_KEY"
```

السلسلة نفسها من [Node SDK](https://docs.orbit.devotel.io/sdks/node.md) — مساعد مُكتب للإنشاء والإطلاق، ومهرب `orbit.request()` للباقي:

```typescript theme={null}
import { Orbit } from "@devotel-orbit/node";

const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY });

const campaign = await orbit.campaigns.create({
  name: "Retail promo — summer sale",
  channel: "sms",
  message_template:
    "Hi {{first_name}}, early access to our summer sale: 25% off through Sunday. Reply STOP to opt out.",
});
const id = campaign.data.id;

// Attach the audience plus the fields the typed helper doesn't carry
// (holdout percent, spend cap, throttle) via a partial update.
await orbit.request("PUT", `/campaigns/${id}`, {
  audience_type: "list",
  audience_id: "list_summerVIPs",
  type: "blast",
  send_time_optimization: "recipient-optimal",
  throttle_rate: 100,
  variables: { campaign_holdout_percent: 10 },
  credit_cap_usd_cents: 50000,
});

const preview = await orbit.request("POST", "/campaigns/audience/preview", {
  audience_type: "list",
  audience_id: "list_summerVIPs",
  channel: "sms",
});

const dryRun = await orbit.request("POST", `/campaigns/${id}/dry-run`);

// Launch — the typed helper auto-generates an idempotency key.
await orbit.campaigns.send(id);

const lift = await orbit.request("GET", `/campaigns/${id}/holdout-lift`);
const roas = await orbit.request("GET", `/campaigns/${id}/roas`);
```

تخبرك استجابة holdout-lift ما إذا كانت رسالة العرض قد تفوقت على تحكم «عدم فعل شيء» (أم لا)، وينسب تقرير ROAS الإيراد لكل لمسة. إمّا أن تدمج الفائز في الحملة التالية أو تراجع القالب وتعيد تشغيل هذه السلسلة.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.