ساخت سناریو
گرافی که هر کاربر تکتک از آن رد میشود: شرط ورود، انتظار، شاخه، و شرط خروج.
کمپین یک پیام را به یک فهرست میفرستد. سناریو گرافی است که هر نفر تکوتنها از آن رد میشود، با سرعت خودش، از لحظهای که چیزی دربارهٔ او درست شده است. دو نفری که با یک ساعت فاصله وارد یک سناریو شدهاند روی دو گره متفاوتاند، منتظر دو تایمر متفاوتاند، و شاید یکیشان از قبل بیرون رفته باشد.
موتوری که گراف را راه میبرد هیچ ورودی و خروجیای ندارد و هیچچیزی را تغییر نمیدهد. یک گراف و یک گره و یک تصویر لحظهای از کاربر میگیرد و برمیگرداند که قدم بعدی چه باید باشد، بهشکل داده. همین است که هر قاعدهٔ شاخهبندی را بدون دیتابیس قابل تست میکند، و اهمیتش این است که یک باگ همینجا پیام اشتباه را به میلیونها نفر میفرستد.
سناریو چیست
سناریو یک گرهٔ ورود دارد، تعدادی گره بعد از آن، و هیچ راه دیگری برای ورود. هر کسی که وارد میشود یک نمونه میگیرد: جای او در یک نسخهٔ مشخص از گراف، بهعلاوهٔ دفترداریای که همراهش میآید.
{
"tenant_id": 7,
"journey_id": 12,
"version": 3,
"user_id": "u_9137",
"current_node": "wait",
"status": "waiting",
"entered_at": "2026-08-01T09:00:00Z",
"updated_at": "2026-08-01T09:00:00Z",
"wake_at": "2026-08-01T10:00:00Z",
"entry_count": 1,
"variant": "",
"is_holdout": false
}
وضعیت نمونه یکی از active و waiting و completed و exited است. دلیل خروج یکی از completed و exit_criteria و max_duration و goal_reached و no_next_node.
نقاط ورود
همهٔ مسیرهای سناریو روی کنترلپلیناند، یعنی همان APIای که پنل با آن حرف میزند. روی میزبان مدیریتی هیچ مسیر سناریویی وجود ندارد.
کنترلپلین از اینترنت مسیردهی نشده است. در استقرار مرجع، api.segmentic.net فقط API مدیریتی را روی شنوندهٔ خودش سرو میکند و شنوندهٔ کنترلپلین عمدا منتشر نشده است. تنها مسیر عمومی به جدول زیر، پروکسی سمت سرور خود پنل است روی https://app.segmentic.net/api/proxy/v1/... که با کوکی نشست کاربر واردشده احراز هویت میکند و بدون آن 401 میدهد. کلید sk_seg_ به آن نمیرسد. پس هرچه در این صفحه میخوانید کاری است که یک آدم در پنل انجام میدهد، از جمله وارد کردن آدمها از API.
| مسیر | مجوز | چه میکند |
|---|---|---|
GET /v1/journeys | journey.read | فهرست، با شمارش زنده |
GET /v1/journeys/{id} | journey.read | گراف منتشرشده بههمراه آمار هر گره. ?version=N برای نسخهٔ قدیمیتر. |
POST /v1/journeys | journey.write | ذخیرهٔ پیشنویس |
GET /v1/journeys/{id}/draft | journey.read | نسخهٔ کاری، ایرادها و هشدارهایش |
POST /v1/journeys/validate | journey.read | کامپایل بدون ذخیره |
POST /v1/journeys/{id}/simulate | journey.read | اجرای آزمایشی روی یک آدم واقعی |
POST /v1/journeys/{id}/publish | journey.publish | ثبت یک نسخه و فعال کردنش |
POST /v1/journeys/{id}/enter | journey.publish | وارد کردن افراد نامبرده |
POST /v1/journeys/{id}/{action} | journey.write | pause و resume و archive |
DELETE /v1/journeys/{id} | journey.write | بردن به سطل بازیافت |
مجوز journey.publish را فقط owner و admin و marketer دارند. از journey.write جداست، چون ویرایش بوم و شروع پیام واقعی به آدم واقعی دو کار جدا هستند.
مسیرهای خواندن فقط وقتی وجود دارند که خوانندهٔ سناریو پیکربندی شده باشد و مسیرهای نوشتن فقط وقتی که ویرایشگر پیکربندی شده باشد؛ enter علاوه بر آن به مسیر ورود داده هم نیاز دارد.
GET /v1/capabilities روی میزبان مدیریتی وقتی سناریوها نصب باشند "journeys": true گزارش میکند. روی آن میزبان هیچ مسیر سناریویی ثبت نشده است. این پرچم را بهمعنی «سناریو از API مدیریتی در دسترس است» نخوانید. نیست، به هیچ شکلی، حتی خواندنی.
پاسخ GET /v1/journeys بهشکل {"journeys": [...]} است، همیشه آرایه و هیچوقت null، و هر سطرش {id, name, status, version, active, waiting} است.
پاسخ GET /v1/journeys/{id} گراف و شمارشها را با هم میدهد:
{
"graph": { "journey_id": 12, "version": 3, "entry_id": "trigger", "nodes": [] },
"stats": {
"trigger": { "entered": 4210, "exited": 0, "suppressed": 0, "waiting": 0 },
"send": { "entered": 3902, "exited": 0, "suppressed": 391, "waiting": 0 }
}
}
این دو عمدا با هم میآیند: گرفتن جداگانهٔ آمار یعنی گراف برای یک لحظه با گرههای خالی دیده میشود، و کل فایده این است که آدم بدون ترک صفحه ببیند قیف از کجا نشت میکند. شکست خواندن آمار نگاشت خالی میدهد نه صفحهٔ خطا. سناریویی که هست ولی هیچوقت منتشر نشده 404 میگیرد با پیام «این سناریو هنوز نسخهٔ منتشرشده ندارد»، که با «چنین سناریویی نیست» یکی نیست و عمدا جور دیگری نوشته شده است.
گراف
| فیلد | JSON | توضیح |
|---|---|---|
| شناسهٔ سناریو | journey_id | |
| نسخه | version | بعد از انتشار تغییرناپذیر است |
| گرهها | nodes | |
| ورود | entry_id | شناسهٔ همان یک گرهای که آدمها از آن وارد میشوند |
| قانون ورود مجدد | entry_rule | once یا every_time یا max_n. خالی یعنی once. |
| سقف ورود | max_entries | همراه max_n |
| کف فاصله | cooldown_hours | بر حسب ساعت، از آخرین ورود |
| بازهٔ ورود مجدد | reentry_window | day یعنی روزی یک بار، به وقت روز حساب. خالی یعنی همان cooldown_hours. |
| شرط خروج | exit_criteria | یک تعریف سگمنت. هر لحظه که بخواند، آدم را هرجا که باشد بیرون میبرد. |
| بیشترین ماندن | max_duration_days | سقف مدتی که کسی میتواند بماند |
شناسهٔ حساب هیچوقت بخشی از سند ذخیرهشده نیست. بارگذارنده آن را میگذارد.
انواع گره
هر گره id و kind دارد، بهعلاوهٔ label و next اختیاری، و دقیق یک شیء پیکربندی که با نوعش میخواند.
{
"id": "wait",
"kind": "wait",
"label": "یک ساعت",
"next": "send",
"wait": { "kind": "duration", "amount": 1, "unit": "hour" }
}
ممکن است x و y هم باشند. آنها جایی هستند که ویرایشگر گره را کشیده و موتور هیچوقت نمیخواندشان. گرافی که آنها را ندارد بیاعتبار نیست؛ بوم خودش چیدمان را حساب میکند.
مقدار خالی برای next مجاز است و سناریو را همانجا تمام میکند. مقدار next که گرهٔ ناموجود را نام ببرد یال آویزان است و کامپایل نمیشود.
هشت نوع گره داریم: trigger و wait و condition و switch و split و action و goal و exit.
شروع
راه ورود. پیکربندی trigger:
| فیلد | معنی |
|---|---|
kind | event یا segment_enter یا segment_exit یا segment_periodic یا attribute_changed یا date یا api |
event | نام رویداد، برای event |
definition | تعریف سگمنت بهشکل خطی، برای نوعهای سگمنتی |
segment_id | شناسهٔ یک سگمنت ذخیرهشده |
trait و value | برای attribute_changed و date. مقدار خالی برای value یعنی هر نوشتن ناخالی روی آن ویژگی. |
filter | مقایسهٔ ویژگی روی تریگر event: gt و gte و lt و lte و eq و neq |
offset_days | برای date: چند روز جلوتر از آن تاریخ شلیک کند. از صفر تا ۳۶۵. |
hours | برای segment_periodic و date: چه ساعتهایی از روز جاروب کند. بین یک تا شش ساعت. |
قاعدهٔ سگمنت بهشکل خطی ذخیره میشود، نه بهشکل ارجاع به یک سگمنت ذخیرهشده. نسخهٔ منتشرشده تغییرناپذیر است و ارجاع این را میشکست: ویرایش آن سگمنت ذخیرهشده بیسروصدا عوض میکرد که چه کسی وارد سناریویی میشود که با قاعدههای دیگری بازبینی و تایید شده بود.
مقدار offset_days فقط *قبل* از تاریخ اجرا میشود، هیچوقت بعد از آن. «سه روز بعد از تولدش» یک گرهٔ انتظار است، و آوردن هر دو در اینجا یعنی یک تأخیر واحد در دو جا نوشته شود که سر ساعت سکوت با هم اختلاف دارند.
مقدار hours ساعتهای روز است نه یک بازه، چون «هر شش ساعت» نسبت به ساعتی که مخاطب با آن زندگی میکند سر میخورد: جاروبی که ساعت 09:00 شروع شده یک هفته بعد جاروب 03:00 است، و پیامهایی که تولید میکند بعد با ساعت سکوت تا صبح نگه داشته میشوند، به دلیلی که هیچکس از روی زمانبندی نمیبیندش.
صبر
kind | فیلدها | رفتار |
|---|---|---|
duration | amount و unit (minute و hour و day و week) | همین حالا بهعلاوهٔ آن مدت بیدار میشود |
until_time | hour از صفر تا ۲۳ و minute | نوبت بعدی آن ساعت دیواری به وقت خود گیرنده |
until_best_time | fallback_hour از صفر تا ۲۳ | ساعتی که این آدم تاریخا در آن تعامل کرده، یا مقدار جایگزین وقتی تاریخچه کافی نیست |
for_event | event و timeout_amount و timeout_unit و on_timeout | تا رسیدن رویداد یا سررسید مهلت پارک میشود |
مقدار fallback_hour روی انتظار بهترینزمان اجباری است و پیشفرض ندارد، چون بهترین زمانی که از سه بار باز کردن حساب شده یک شیر یا خط است که لباس آمار پوشیده، و پیشفرض شدن بیسروصدای نیمهشب یعنی ساعت سه بامداد به کسانی پیام میرود که پلتفرم کمترین شناخت را از آنها دارد.
انتظار for_event وقتی رویداد برسد از next ادامه میدهد و وقتی تایمر زودتر شلیک کند از on_timeout. مقدار خالی برای on_timeout به next برمیگردد، پس هر دو مسیر به هم میرسند.
شرط و چندراهه
شرط یعنی بله یا خیر:
{
"id": "bought",
"kind": "condition",
"condition": {
"definition": { "root": {} },
"on_true": "thanks",
"on_false": "remind"
}
}
چندراهه یعنی چند مسیر، بهترتیب بررسی میشود و اولین تطابق برنده است:
{
"id": "plan",
"kind": "switch",
"switch": {
"cases": [
{ "label": "premium", "definition": { "root": {} }, "next": "vip" },
{ "label": "paid", "definition": { "root": {} }, "next": "standard" }
],
"default": "free"
}
}
فیلد default اجباری است. بدون آن هرکسی که با هیچ حالتی نخواند همانجا از سناریو بیرون میافتد، که عین یک باگ دیده میشود و تا وقتی گزارش مخاطب کم نیاید نامرئی است.
ترتیبی است نه جمعکننده، چون همپوشانی قاعدهها حالت عادی است: آدمهای طرح ویژه همان آدمهایی هم هستند که خرید کردهاند، و فهرست مرتب تنها خوانشی است که در آن نویسنده تصمیم میگیرد کدامیک به حساب میآید.
هر دو از همان زبان تعریف سگمنت استفاده میکنند که یک مخاطب ذخیرهشده استفاده میکند. بخش سگمنت را ببینید.
وقتی ارزیاب سگمنت در دسترس نباشد، شرط مسیر on_false و چندراهه مسیر default را میگیرد. این خوانش محافظهکارانه است و بهجای خطا همین اتفاق میافتد.
تقسیم
{
"id": "test",
"kind": "split",
"split": {
"branches": [
{ "weight": 1, "next": "arm_a", "variant": "A" },
{ "weight": 1, "next": "arm_b", "variant": "B" }
],
"holdout_percent": 10,
"on_holdout": "end"
}
}
مقدار weight سهم است نه درصد. موتور خودش نرمال میکند، پس بازاریابی که یک شاخه را ویرایش میکند نمیتواند جمع را روی نود و هفت جا بگذارد. مقدار holdout_percent بین صفر و ۱۰۰ است.
تخصیص با هش انجام میشود نه با قرعه: گروه کنترل از (user_id, node_id) و شاخه از (user_id, node_id + ":branch") میآید، پس این دو مستقلاند و هر دو در تلاش مجدد پایدار میمانند. کاربری که موقع تلاش مجدد بین شاخهها میپرید نتیجه را بیمعنی میکرد.
کاربر گروه کنترل که مسیر on_holdout نداشته باشد از شاخهٔ اول میرود با ارسالهای متوقفشده. بخش گروه کنترل را ببینید.
اقدام
پنج نوع:
kind | لازم دارد | چه میکند |
|---|---|---|
send | channel یا channels، و برای هرکدام یک قالب | پیام تحویل میدهد |
update_trait | trait و value | روی پروندهٔ کاربر مینویسد |
webhook | url | نقطهٔ ورود شما را صدا میزند |
add_to_segment | segment_id | کاربر را به یک فهرست اضافه میکند |
enter_journey | target_journey_id | او را به سناریوی دیگری تحویل میدهد |
گرهٔ ارسال میتواند یک کانال نام ببرد (channel و template_id) یا چند تا (channels، آرایهای از {channel, template_id}) با channel_mode خالی برای جایگزینی یا "all" برای پخش همزمان. حالت جایگزینی هرکدام را بهترتیب امتحان میکند و روی اولینی که به آدم میرسد میایستد. فقط امتناعی که شکل کانال دارد باعث رفتن به بعدی میشود: دستگاه نیست، شماره نیست، این رسانه خاموش است. امتناعی که دربارهٔ خود آدم است، مثل لغو اشتراک و سقف تعداد و ساعت سکوت، گره را برای او کاملا متوقف میکند، چون امتحان کردن کانال بعدی یعنی دنبال راه دور زدن آن جواب گشتن.
مقدارهای priority و budget روی گرهاند نه روی سناریو، چون یک جریان هر دو نوع پیام را دارد: «سفارشتان راه افتاد» و «شاید این را هم بپسندید» در یک سناریو هستند و ادعای یکسانی روی توجه کسی ندارند.
اقدام enter_journey نمیتواند سناریوی خودش را هدف بگیرد.
هدف و پایان
گرهٔ هدف {"event": "...", "window_days": N} دارد. ایستگاه تبدیل است؛ window_days سقف انتساب است، چون خریدی که شش ماه بعد اتفاق افتاده کار این سناریو نیست.
گرهٔ پایان پایانی است و پیکربندی ندارد.
یک تعریف کامل
سناریوی سبد رها شده، با همان JSONای که POST /v1/journeys میپذیرد:
{
"name": "سبد رها شده",
"graph": {
"journey_id": 1,
"entry_id": "trigger",
"nodes": [
{
"id": "trigger", "kind": "trigger", "label": "افزودن به سبد", "next": "wait",
"trigger": { "kind": "event", "event": "product_added_to_cart" }
},
{
"id": "wait", "kind": "wait", "label": "یک ساعت صبر", "next": "send",
"wait": { "kind": "duration", "amount": 1, "unit": "hour" }
},
{
"id": "send", "kind": "action", "label": "یادآوری", "next": "end",
"action": { "kind": "send", "channel": "push", "template_id": 3 }
},
{ "id": "end", "kind": "exit" }
]
}
}
یک جاروب دورهای با کف فاصله و شرط خروج. همین ترکیب، یعنی روزی دو بار جاروب کن، به هیچکس بیشتر از هفتهای یک بار پیام نده، و هرکس ثبتنام کرد را رها کن، شکلی است که هیچ هشداری تولید نمیکند:
{
"journey_id": 1,
"entry_id": "trigger",
"entry_rule": "every_time",
"cooldown_hours": 168,
"exit_criteria": { "root": {} },
"nodes": [
{
"id": "trigger", "kind": "trigger", "label": "جاروب", "next": "send",
"trigger": {
"kind": "segment_periodic",
"definition": { "root": {} },
"hours": [11, 18]
}
},
{
"id": "send", "kind": "action", "label": "یادآوری", "next": "end",
"action": { "kind": "send", "channel": "push", "template_id": 3 }
},
{ "id": "end", "kind": "exit" }
]
}
پاسخ POST /v1/journeys این است: {"id": 12, "version": 4}. پیشنویس اجازه دارد ناقص باشد. یک گرهٔ تنها بدون هیچ یالی بیاعتراض ذخیره میشود، چون بازاریاب یک جریان را در چند نشست میسازد و رد کردن نصف گراف یعنی از دست دادن کارش. جایی که گراف باید سالم باشد، انتشار است.
شکستها: 400 malformed JSON و 400 name is required و 400 graph is required و 503 could not save journey.
آدمها چطور وارد میشوند
قانون ورود مجدد تصمیم میگیرد چه کسی میتواند دوباره شروع کند:
entry_rule | رفتار |
|---|---|
once (و خالی) | هیچوقت دوباره، هر اتفاقی که بار اول افتاده باشد |
every_time | دوباره، مشروط به کف فاصله |
max_n | تا وقتی entry_count به max_entries برسد |
کسی که همین حالا active یا waiting است هیچوقت دوباره وارد نمیشود، تحت هیچ قانونی. شروع دوبارهٔ او یعنی کل جریان را دو بار بهطور موازی میگیرد. مبدأ cooldown_hours آخرین بهروزرسانی نمونهٔ قبلی است، نه زمان شروعش.
کف فاصله دو خوانش دارد
cooldown_hours یک مدت است: تا N ساعت از آخرین ورود نگذرد، کسی دوباره وارد نمیشود. reentry_window: "day" یک بازهٔ تقویمی است: هر نفر روزی حداکثر یک بار، و شمارنده سر نیمهشب صفر میشود نه ۲۴ ساعت بعد از ورود قبلی. مبدأ بازهٔ تقویمی زمان ورود نمونهٔ قبلی است، نه بهروزرسانیاش، تا فاصله به طول خود جریان وابسته نشود.
این دو یک چیز نیستند. یک پیمایش با ساعتهای ۱۰ و ۱۸ و cooldown_hours برابر ۲۴، یک شب ساعت ۱۹ تعداد ۲۱۱۰ نفر را وارد کرد و روز بعد هر دو نوبتش داخل همان ۲۴ ساعت افتاد، پس ۱۵ نفر وارد شدند در برابر خط پایهٔ ۲۱۹۵. آن گروه به ساعتی که اولین بار وارد شده قفل میشود و از آن به بعد روزهای کامل را از دست میدهد. یادآوری روزانه یعنی «روزی یک بار»، و هیچ مدتی این را نمیگوید.
روز از آن حساب است، به وقت تهران، همان روزی که سقف فرکانسی روزانه در آن شمرده میشود. این دو فیلد جایگزین همدیگرند نه مکمل هم: گرافی که هم reentry_window داشته باشد و هم cooldown_hours دو جواب دارد، پس POST /v1/journeys/validate آن را ایراد گزارش میکند و پنل منتشرش نمیکند. اگر با این حال جایی ذخیره شود، موتور بازه را اجرا میکند و ساعتهای کنارش هیچ کاری نمیکنند.
اینکه تریگر واقعا چطور کسی را وارد میکند به نوعش بستگی دارد، و همین تفاوتها جایی است که آدمها گیر میکنند.
event آنی است. رویداد روی گذرگاه میآید، ورکر همهٔ گرافهای منتشرشده را بررسی میکند و تطابق، آدم را وارد میکند. مقایسههای filter همینجا اعمال میشوند. سناریویی که منتظر یک رویداد معمولی است هیچوقت با رویداد رزروشدهٔ ورودی که پایینتر توضیح داده شده وارد نمیشود.
attribute_changed فقط با یک فراخوان identify میخواند که واقعا آن ویژگی را نوشته باشد، با تطابق اختیاری روی مقدار دقیق. از خود رویداد identify ارزیابی میشود، پس آنی واکنش میدهد و هیچوقت مقدار قدیمی پرونده را با تغییر اشتباه نمیگیرد.
segment_enter و segment_exit و segment_periodic و date را یک پویشگر پیدا میکند نه یک رویداد. هیچکس «از سگمنت مشتریهای خفته خارج شد» را منتشر نمیکند؛ این چیز درست میشود چون خریدی که سه هفته پیش انجام شده از یک بازه رد شده، و تنها راه فهمیدنش این است که دو بار نگاه کنی و مقایسه کنی.
تریگر سگمنتی آنی نیست و ما هم آنی معرفیاش نمیکنیم. پویشگر پیشفرض هر پنج دقیقه عضویت را دوباره میخواند. تأخیر به همین بازه محدود است. بازاریابی که «همان لحظهای که خرید کرد» را میخواهد، تریگر رویدادی میخواهد.
سه ویژگی پویشگر باربر هستند:
- اولین پویش هیچکس را وارد نمیکند. فقط ثبت میکند چه کسانی داخل سگمنتاند و میایستد. بدون این قفل، انتشار یک سناریوی بازگرداندن برای چهارصد هزار مشتری خفته به همهشان در یک بازهٔ پویش پیام میدهد، و گراف بعدش کاملا درست به نظر میرسد.
- ورود از همان مسیری میرود که پرش
enter_journeyمیرود، پس قانون ورود و کف فاصله و شرط خروج را همان کدی اعمال میکند که از قبل اعمالشان میکرد. - بریدگی ثبت میشود، هیچوقت بیصدا نیست. یک تفاضل تا
250000عضو و یک جاروب تا5000000عضو محدود است و هر دو صفحههای5000تایی میخوانند. سگمنتی بزرگتر از آنچه پویش میتواند بخواند یعنی سناریویی که بیسروصدا از دیدن آدمهای بعد از آن سقف دست کشیده است.
ساعتهای جاروب به وقت تهران خوانده میشوند، چون «ساعت ۱۱ و ۱۸ جاروب کن» جملهای دربارهٔ روز مخاطب است.
تریگر date عبارت «سه روز قبل از تولدش» را به «تعداد روز تا تولدش دقیق سه است» ترجمه میکند و اگر خودش تعریفی داشته باشد با AND به آن میچسباند.
هر ورودی غیررویدادی، از جمله ورودی API، بهشکل رویداد رزروشدهٔ journey_enter_requested با ویژگی عددی journey_id سفر میکند. برای همین یک ورود از API، حذف تکراری و قانون ورود مجدد و شرط خروج و دفترداری نمونه را به ارث میبرد و مسیر ورود دومی نمیسازد که تا روزی که با اولی اختلاف پیدا کند با آن موافق باشد.
مجموعهٔ گرافهای منتشرشده هر سی ثانیه دوباره بارگذاری میشود، پس انتشار و توقف و ازسرگیری ظرف نیم دقیقه به مسیر ورود میرسد.
انتظار و اینکه چقدر بادوام است
تایمرها سطرهای بادوام در پستگرساند، نه زمانبندی در حافظه. ریاستارت ورکر، استقرار تازه، کرش: تایمر همچنان سر جایش است و همچنان شلیک میکند.
- تایمرها به تفکیک دقیقه سطلبندی میشوند و همین است که خواندنشان را ارزان میکند. با ده میلیون آدم که روی گرهٔ «سه روز صبر کن» پارک شدهاند، به ازای هر تیک یک پارتیشن کوچک خوانده میشود نه یک ایندکس روی همهٔ تایمرهای آینده.
- خواندن هر یک ثانیه با دستهٔ ۱۰۰۰تایی انجام میشود، هرچند سطلها دقیقهای هستند، چون کسی که «پنج دقیقه صبر کن» گذاشته تقریب پنج دقیقه انتظار دارد و یک دقیقه گرد کردن روی آن دیده میشود.
- زمانبندی تازه، هر تایمر موجود برای همان آدم در همان سناریو را جایگزین میکند. یک نفر نمیتواند همزمان در دو جا منتظر باشد.
- گرفتن تایمر اتمی است. تایمر گرفتهشده به ورکر دوم داده نمیشود.
- خطا هنگام اجرای یک تایمر آن را گرفته و پردازشنشده رها میکند و پاس بعدی برشمیدارد. شکست یک نفر دسته را قطع نمیکند: بقیه آدمهای بیربطی هستند که پیامشان هم همین حالا سررسیده است.
- وضعیت قبل از فرستادن اثرها ذخیره میشود. کرش بین این دو، پیامی را دوباره میفرستد که لایهٔ تحویل تکراریاش را حذف میکند، بهجای اینکه گمش کند. گم شدن همان شکستی است که بعدش هیچ ردی از خودش ندارد.
انتظار ساعت دیواری به وقت خود گیرنده حساب میشود، پس «تا ساعت نه صبح صبر کن» یعنی نه صبح او. انتظار بهترینزمان ساعت تعامل آدم را همان لحظه که لازم دارد میخواند و شکست این خواندن بیسروصدا به fallback_hour برمیگردد.
متوقف کردن یک سناریو، آدمهایی را که از قبل داخلش هستند متوقف نمیکند. توقف، سناریو را از مجموعهٔ منتشرشده بیرون میبرد، پس کسی تازه وارد نمیشود. اما تایمرها با شناسه و نسخه از انبار حل میشوند و وضعیت سناریو بررسی نمیشود، پس کسی که روی انتظار سهروزه پارک شده بیدار میشود و پیامش را میگیرد. اگر لازم است ارسالها بایستد، کانال یا حساب را خاموش کنید که کلیدهای قطع برای همین هستند. بخش جهت شکست را ببینید.
بیرون رفتن
سه راه خروج، که هر بار نمونه جلو میرود به همین ترتیب بررسی میشوند:
exit_criteriaبر همهچیز مقدم است، آدم هرجا که باشد. همین است که نمیگذارد سناریوی سبد رها شده به کسی که خریدش را انجام داده گیر بدهد.max_duration_daysکه سقف ماندن را میگذارد. کسی که پشت یک انتظار رویدادی گیر کرده در غیر این صورت بینهایت در سناریو میماند.- تمام شدن گراف: یک گرهٔ پایان، یک
nextخالی، یا انتهای طبیعی جریان.
بعد موتور گرهها را میپیماید، حداکثر ۱۰۰ قدم در هر فراخوان. بیشتر از آن خطای سقف قدم میدهد. کامپایلر از قبل حلقهٔ بدون انتظار را رد میکند؛ این یکی تور ایمنی زمان اجراست، چون موتوری که اینجا حلقه بزند قبل از اینکه کسی متوجه شود هزاران بار همان پیام را میفرستد.
دلیل خروج روی نمونه ثبت میشود، تا بازاریاب ببیند سناریو آدمها را تبدیل کرده یا فقط مهلتشان تمام شده.
نسخهها
نسخهٔ منتشرشده تغییرناپذیر است و هر آدم به نسخهای سنجاق میشود که با آن وارد شده. کسی که با نسخهٔ سه وارد شده با نسخهٔ سه تمام میکند، حتی بعد از انتشار نسخهٔ چهار، چون هدایت دوبارهٔ آدم نیمهکاره به گراف عوضشده یعنی انداختنش روی گرهای که دیگر آن معنایی را ندارد که موقع رسیدنش داشت.
تایمر هم نسخه را با خودش حمل میکند، پس بیدارباش همان گرافی را حل میکند که آدم با آن وارد شده بود.
مسیر GET /v1/journeys/{id}?version=N نسخهٔ قدیمی را میخواند. نسخهٔ صفر، یا نبودن این پارامتر، یعنی هرچه الان منتشر است.
اعتبارسنجی پیش از انتشار
مسیر POST /v1/journeys/validate بدنهٔ {"graph": {...}} میگیرد و 200 میدهد:
{ "valid": true, "problems": [], "warnings": [] }
فقط وقتی بدنه خوانده نشود یا گراف null باشد 400 میگیرید. بقیهٔ حالتها 200 است با یک فهرست.
problems جلوی انتشار را میگیرد، warnings نمیگیرد. این دو بهجای اینکه در هم ادغام شوند جدا نگه داشته شدهاند، چون دو واکنش متضاد میخواهند. ایراد یعنی «این نمیتواند برود». هشدار یعنی «این میرود، و این کاری است که خواهد کرد». رد کردن انتشار یک گراف هشداردار، شکلهایی را میبست که گاهی درستاند، و ریختنشان در یک فهرست به بازاریاب یاد میداد که فهرست توصیهای است، و همین خوانش است که یک یال آویزان را منتشر میکند.
همهٔ ایرادها فهرست میشوند نه فقط اولی، چون بازاریابی که هر بار یک یال آویزان را درست میکند و بینشان یک رفتوبرگشت دارد، کار را رها میکند.
| شرط | پیام |
|---|---|
| هیچ گرهای ندارد | این سناریو هیچ گرهای ندارد |
| گرهای بدون شناسه | یکی از گرهها شناسه ندارد |
| شناسهٔ گرهٔ تکراری | گره «%s» تکراری است |
| نقطهٔ شروع تعیین نشده | نقطه شروع مشخص نشده است |
| نقطهٔ شروع به جایی اشاره نمیکند | نقطه شروع به گرهای اشاره میکند که وجود ندارد |
| یالی به گرهٔ حذفشده | خروجی «%s» از گره «%s» به گرهای میرود که حذف شده است |
| هیچ گرهٔ اقدامی ندارد | این سناریو هیچ کاری انجام نمیدهد؛ یک گره ارسال اضافه کنید |
| تریگر رویدادی بدون رویداد | گره شروع «%s» رویداد ورود ندارد |
| تریگر سگمنتی بدون تعریف | گره شروع «%s» سگمنتی برای بررسی ندارد |
| جاروب بدون ساعت | گره شروع «%s» ساعت جاروب ندارد؛ بدون آن هیچوقت اجرا نمیشود |
| بیش از شش ساعت جاروب | گره شروع «%s» بیش از %s بار در روز جاروب میکند |
| تریگر تاریخی بدون ویژگی تاریخ | گره شروع «%s» مشخص نکرده روی چه تاریخی اجرا شود |
| فاصلهٔ تاریخ بیرون از صفر تا ۳۶۵ | گره شروع «%s» فاصلهٔ نامعتبری تا آن تاریخ دارد |
| چندراهه بدون حالت | گره چندراهه «%s» هیچ راهی ندارد |
| چندراهه بدون پیشفرض | گره چندراهه «%s» مسیر پیشفرض ندارد؛ کسی که هیچ شرطی برایش برقرار نباشد همینجا از سناریو میافتد |
| اقدام تنظیمنشده | گره «%s» تنظیم نشده است |
enter_journey بدون مقصد | گره «%s» مقصدی برای پرش ندارد |
| ارسال بدون کانال | گره ارسال «%s» کانال ندارد |
| ارسال با کانال خالی | گره ارسال «%s» یک کانال خالی دارد |
| ارسال بدون قالب | گره ارسال «%s» هیچ متنی برای فرستادن ندارد |
| ارسال چندکاناله با یک قالب کم | گره ارسال «%s» برای کانال %s متنی ندارد |
| انتظار تنظیمنشده | گره انتظار «%s» مدت ندارد |
| انتظار رویدادی بدون مهلت | گره انتظار «%s» مهلت ندارد؛ کاربری که آن رویداد را انجام ندهد برای همیشه در سناریو میماند |
| تقسیم بدون شاخه | گره تقسیم «%s» هیچ شاخهای ندارد |
| شرط تنظیمنشده | گره شرط «%s» شرطی ندارد |
هرچه این فهرست جا بیندازد را کامپایلر میگیرد، بالاتر از همه حلقهای که هیچ گرهٔ انتظاری داخلش نیست، که رد میشود. انتظار حلقه را میشکند چون باید زمان بگذرد.
کامپایلر علاوه بر فهرست بالا اینها را هم اجبار میکند: duration مقدار amount مثبت و unit شناختهشده میخواهد؛ until_time ساعتی بین صفر و ۲۳ میخواهد؛ until_best_time مقدار fallback_hour بین صفر و ۲۳ میخواهد که بهجای بریده شدن رد میشود؛ تعریف هر حالت چندراهه باید سگمنت معتبری باشد؛ تقسیم دستکم یک شاخه و بدون وزن منفی و با جمع مثبت و holdout_percent بین صفر و ۱۰۰ میخواهد؛ update_trait مقدار trait میخواهد؛ webhook مقدار url؛ add_to_segment مقدار segment_id؛ و هدف یک event.
یک ناهمگونی را بدانید: کامپایلر برای گرهٔ ارسال قالب لازم نمیداند. فهرست problems ویرایشگر لازم میداند. پس گرافی میتواند کامپایل شود و باز هم منتشرشدنی نباشد.
هشدارها فقط به تریگرهای segment_periodic و date مربوطاند:
| شرط | هشدار |
|---|---|
تریگر تاریخی با قانون ورود once | این سناریو هر نفر را فقط یک بار میپذیرد، پس برای هر کاربر فقط یک سال اجرا میشود |
تریگر تاریخی با every_time و کف فاصلهٔ کمتر از نود روز | کف فاصله برای یک مناسبت سالانه کوتاه است |
جاروب با قانون ورود once | این سناریو هر نفر را فقط یک بار میپذیرد، پس جاروب دورهای فقط کسانی را وارد میکند که تا امروز وارد نشدهاند |
جاروب با every_time و بدون کف فاصله | با قانون ورود «هر بار» و بدون کف فاصله، هر جاروب به همهٔ اعضای سگمنت پیام میدهد |
جاروب بدون exit_criteria | این سناریو شرط خروج ندارد؛ کسی که کار موردنظر را انجام دهد تا وقتی از سگمنت خارج نشود همچنان هدف جاروب است |
| سناریوی درحالاجرای دیگری همین امضای ورود را دارد | سناریوی «%s» همین شرط ورود را دارد؛ یک نفر از هر دو پیام میگیرد |
هشدار همپوشانی، اثر انگشتی از چیزی که تریگر دم در میپرسد را مقایسه میکند و عمدا هرچه بعد از آن میآید را نادیده میگیرد. سناریو هیچوقت دربارهٔ خودش هشدار نمیدهد، و شکست این جستجو بیصداست نه کشنده.
انتشار، توقف، حذف
مسیر POST /v1/journeys/{id}/publish پیشنویس ذخیرهشده را دوباره اعتبارسنجی میکند بهجای اینکه به آخرین بررسی کلاینت اعتماد کند. نظر مرورگر یک راحتی است؛ این دروازهای است که تصمیم میگیرد پیامها شروع به رفتن کنند یا نه.
{
"error": "این سناریو هنوز آمادهٔ انتشار نیست",
"problems": ["گره ارسال «send» هیچ متنی برای فرستادن ندارد"]
}
موفقیت 200 است با {"version": 4}. اگر پیشنویس خوانده نشود 404 و اگر خود انتشار شکست بخورد 503.
مسیرهای POST /v1/journeys/{id}/pause و /resume و /archive وضعیت را روی paused و active و archived میگذارند و {"status": "paused"} جواب میدهند. هر اقدام دیگری 400 unknown action است.
مسیر DELETE /v1/journeys/{id} یک حذف نرم به سطل بازیافت است که سی روز نگه داشته میشود و با POST /v1/recycle/journey/{id}/restore برمیگردد. سناریویی که درحال اجرا یا زمانبندیشده است 409 میگیرد و به شما میگوید اول متوقفش کنید. سناریوی از قبل حذفشده یا ناموجود 404 میگیرد، چون از این نقطه «قبلا حذف شده» و «هیچوقت نبوده» یک جواب واحد دارند: فراخوان دارد به صفحهای قدیمی نگاه میکند.
وارد کردن آدمها از بکاند خودتان
نقطهٔ ورودی دقیق برای همین کار وجود دارد، و در استقرار مرجع بکاند شما نمیتواند به آن برسد. هر دو نیمه درستاند و نیمهٔ دوم همان است که یک بعدازظهر خرج برمیدارد، پس اول همان را میگوییم.
مسیر POST /v1/journeys/{id}/enter روی کنترلپلین است. شنوندهٔ کنترلپلین به اینترنت منتشر نشده: api.segmentic.net فقط API مدیریتی را حمل میکند و روی آن هیچ مسیر سناریویی ثبت نشده است. تنها مسیر عمومی، پروکسی سمت سرور خود پنل است که کوکی نشست کاربر واردشده را میخواهد و کلید sk_seg_ را رد میکند. پس امروز این یک کار داخل پنل و یک یکپارچهسازی درونشبکه است، نه یک API عمومی.
خود نقطهٔ ورود:
POST /v1/journeys/12/enter
Content-Type: application/json
{ "user_ids": ["u_9137", "u_4410"] }
{
"accepted": 2,
"status": "queued",
"note": "ورود پس از پردازش رویداد انجام میشود؛ قوانین ورود مجدد و شرط خروج همچنان اعمال میشوند."
}
- گرهٔ ورود سناریو باید تریگر
apiباشد. هر چیز دیگری409میگیرد. بدون این بررسی، فراخوان موفق میشود، رویداد منتشر میشود، ورکر تطابق نمیدهد، و به فراخوان دربارهٔ چیزی که هیچوقت اتفاق نمیافتد گفته شده «پذیرفته شد». - مجوز
journey.publishاست نهjourney.write. این گراف را ویرایش نمیکند؛ باعث میشود پیام واقعی به آدم نامبرده برود. - هم
{"user_id": "u1"}را میپذیرد و هم{"user_ids": ["u1","u2"]}و هر دو را با هم. خالیها و تکراریها حذف میشوند و تکرار یک نفر در یک فراخوان یک نفر است. - حداکثر ۵۰۰ نفر در هر فراخوان. بیشتر از آن
400 at most 500 people per callاست. وارد کردن یک تصمیم نفربهنفر است، و درخواستی که بتواند صد هزار نفر را نام ببرد یک کمپین است که لباس API پوشیده، بدون پیشنمایش مخاطب و بدون گزارش پوشش یک کمپین. - سقف بدنه یک مبیبایت است و فیلد ناشناخته رد میشود.
عدد accepted میگوید گذرگاه رویداد چند تا را گرفت، نه اینکه چند نفر سناریو را شروع کردند. ورکر همچنان قانون ورود مجدد و کف فاصله و شرط خروج را اعمال میکند، پس بعضی از آن آدمها شروع نخواهند کرد. اسم این فیلد از روی چیزی انتخاب شده که این نقطهٔ ورود واقعا میتواند قولش را بدهد.
هر نفر به یک پاکت track تبدیل میشود با شناسهٔ پیامی به شکل jenter-{journeyID}-{userID}-{unixSeconds}، پس تلاش مجدد داخل همان ثانیه در کالکتور تکراری حساب میشود.
شکستها: 400 برای شناسهٔ بد سناریو، بدنهٔ ناخوانا، فیلد ناشناخته یا فهرست خالی کاربران؛ 404 وقتی پیشنویس خوانده نشود؛ 409 وقتی ورود تریگر API نباشد؛ 503 could not queue the entry.
شبیهسازی
مسیر POST /v1/journeys/{id}/simulate پیشنویس را روی یک آدم واقعی اجرا میکند و هر تصمیم را گزارش میدهد. هیچچیز فرستاده نمیشود و هیچچیز ذخیره نمیشود.
{ "user_id": "u_9137" }
مقدار خالی user_id باعث میشود سرور خودش یک پروندهٔ اخیر را انتخاب کند. فرستادن graph یعنی چیزی که روی بوم است تست شود نه چیزی که آخرین بار ذخیره شده.
{
"steps": [
{ "node_id": "trigger", "label": "افزودن به سبد", "kind": "visited", "detail": "..." },
{ "node_id": "wait", "label": "یک ساعت صبر", "kind": "wait", "detail": "..." },
{ "node_id": "send", "label": "یادآوری", "kind": "effect", "detail": "...",
"effect": { "kind": "send", "channel": "push", "template_id": 3, "node_id": "send" } }
],
"reached": true,
"outcome": "...",
"sends": 1,
"simulated_waits": 1,
"problems": [],
"user_id": "u_9137"
}
نوع هر قدم یکی از visited و branch و effect و wait و end و error است. یک قدم میتواند suggestion هم داشته باشد وقتی چیزی در آن ایراد دارد.
کاری که یک بررسی ساختاری نمیتواند بکند و این میکند:
- انتظارها شتاب میگیرند و
simulated_waitsمیگوید چند تا. گرهٔ «دو روز صبر کن» در غیر این صورت دکمهٔ تست را دقیق روی همان سناریوهایی بیفایده میکرد که بیشتر از همه باید بررسی شوند. انتظارfor_eventمسیر «رویداد رسید» را میگیرد. - شرط خروج اول از همه بررسی میشود، عین کاری که موتور میکند، چون سناریویی که شرط خروجش از قبل برای همهٔ واردشوندگان برقرار است رایجترین شکل «چرا هیچکس این را نگرفت» است: جریان درست است و هر واردشونده دم در بیرون میرود.
- شرطها روی آدم واقعی و با همان تطبیقدهندهای ارزیابی میشوند که ورکر استفاده میکند. شکست تطبیقدهنده بهشکل «این شاخه گرفته نشد» گزارش میشود نه قطع کردن رد.
- فهرست
problemsهمان فهرستی است که دکمهٔ انتشار میبیند، پس پیمودن تمیز یک شاخه بهمعنی منتشرشدنی بودن خوانده نمیشود. - سقفش ۱۰۰ قدم است.
مجوزش journey.read است. گذاشتن یک اجرای آزمایشی پشت مجوز انتشار یعنی کسی که نمیتواند منتشر کند، کار خودش را هم نمیتواند بررسی کند.
شکستها: 400 برای شناسهٔ بد یا بدنهٔ ناخوانا؛ 409 no profile is available for a test run yet وقتی حساب هیچ پروندهای برای انتخاب ندارد؛ 503 وقتی جستجوی کاربر آزمایشی شکست بخورد؛ 404 وقتی نه گراف فرستاده شده باشد و نه پیشنویس ذخیرهشدهای باشد.
گروه کنترل داخل سناریو
آدم گروه کنترل کل گراف را میپیماید و هیچ پیامی نمیگیرد. همین است که اثر سناریو را قابل اندازهگیری میکند، و قاعدهٔ جالب هم همینجاست: دو بازو فقط باید در آنچه فرستاده شده با هم فرق کنند.
| اثر | برای گروه کنترل |
|---|---|
send | ثبت و متوقف میشود، دقیق در همان نقطهای که یک ارسال واقعی متوقف میشد |
update_trait | اعمال میشود |
add_to_segment | اعمال میشود |
webhook | رد میشود |
enter_journey | رد میشود |
ویژگی و عضویت سگمنت، وضعیتاند. رد کردنشان یعنی گروه کنترل از یک راه دوم هم با گروه هدف فرق کند، و عدد اثربخشی بعدش هر دو تفاوت را با هم اندازه میگرفت. وبهوک و پرش اثر بیرونیاند: اجرایشان آدم را در یک سطح وفاداری ثبت میکند، یک تیکت باز میکند، یا سناریوی بعدی را شروع میکند، که دقیق همان چیزی است که گروه کنترل برای ندادنش وجود دارد.
ارسال از داخل سناریو
گرهٔ ارسال پیام را به همان مسیر تحویلی میدهد که کمپین میدهد، پس هر قاعدهای در رضایت و سقف روی آن هم اعمال میشود.
- دستهٔ پیشفرض یک ارسال سناریو
marketingاست. دستهٔ خود قالب آن را کنار میزند، و همان فیلدی است که باید برای پیام سفارشمانند داخل یک سناریو تنظیم کنید. - شناسهٔ پیام
j{journey_id}.v{version}.{node_id}.{user_id}.e{entry_count}است، و برای کانال دوم به بعد یک گرهٔ چندکاناله نام کانال هم به آن اضافه میشود. دو کانالی که به یک نفر میرسند دو پیاماند، و شناسهٔ مشترک باعث میشد دومی تلاش مجدد اولی حساب و بیسروصدا دور ریخته شود. - ارسالی که لایهٔ حکمرانی به تعویق بیندازد بهعنوان ارسال معوق در صف میرود، نه اینکه دوباره روی تایمر سناریو کوک شود. سناریو جلو میرود؛ پیام بعدا آزاد میشود.
- بدون انبار تعویق پیکربندیشده، پیام معوق سناریو با یک هشدار دور ریخته میشود. این چیزی است که روی نصبی که خودتان بالا نیاوردهاید ارزش بررسی دارد.
گرهٔ ارسالی که روی کانال webhook نوشته شده باشد، برای هر کسی که به آن برسد شکست میخورد. کامپایلر فقط بررسی میکند که رشتهٔ کانال خالی نباشد، پس "channel": "webhook" اعتبارسنجی میشود، منتشر میشود و اجرا میشود. در کل لایهٔ تحویل هیچ فرستندهای برای وبهوک نیست: این مقدار در واژگان کانالهای قابلنوشتن هست و در هیچ چیزی که تحویل بدهد نیست. هر ورودی با failed و «این کانال پیکربندی نشده است» برمیگردد، یکی به ازای هر نفر، و گراف درست به نظر میرسد. اقدام webhook در جدول بالا چیز دیگری است و کار میکند: نقطهٔ ورود شما را صدا میزند بهجای اینکه پیام بفرستد. فقط push و sms و email و webpush و inapp و bale و eitaa و rubika فرستنده دارند.
شخصیسازی از روی رویداد راهانداز
ارسال سناریو از همان سه منبعی رندر میشود که ارسال تراکنشی از آن رندر میشود، بهعلاوهٔ یک منبع دیگر: ویژگیهای رویدادی که این قدم را باعث شده، زیر فضای نام event..
{{event.rival}} از تو جلو زد، الان رتبه {{event.rank}} هستی
رویداد یا همانی است که موقع ورود با راهانداز جور شده، یا همانی که انتظار برای رویداد را تمام کرده و آدم را بیدار کرده است. هرکدام که این قدم را باعث شده باشد.
این فضای نام هیچوقت با چیزی ادغام نمیشود، و نکته دقیقا همین است. یک ویژگی ذخیرهشده به نام rank و یک ویژگی رویداد به نام rank دو واقعیت متفاوتاند و معمولا هر دو در یک جمله لازماند: {{rank}} همان ویژگی پروفایل است و {{event.rank}} ویژگی رویداد، و هیچکدام دیگری را پنهان نمیکند. event. تنها پیشوندی هم هست که رندرکننده هرگز آن را برنمیدارد، پس {{event.rank}} ای که پر نشده باشد یک متغیر بیمقدار است، نه اینکه بیسروصدا مقدار ویژگی پروفایل را بگیرد.
قدمی که رویداد ندارد، هیچکدام از این متغیرها را ندارد. انتظاری که تمام میشود، راهانداز تاریخی و سوییپ سگمنت همه بدون رویداد ادامه میدهند، و این شامل هر ارسالی است که بعد از یک گرهٔ انتظار نشسته باشد. قالبی که یکی از اینها را نام ببرد آنوقت متغیر بیمقدار دارد، پس ارسال با missing_personalisation متوقف میشود و کلیدهای غایب در لاگ ورکر نام برده میشوند. جایگزینش پوشی است که « از تو جلو زد» میخواند و برگشتپذیر نیست. اگر جمله بدون آن مقدار هم کامل است، برای جاینگهدار یک مقدار جایگزین بگذارید، و اگر کامل نیست، ارسال را قبل از انتظار بگذارید.
رویداد روی سطر نمونه نوشته نمیشود. همان جا و در همان قدم خوانده و بعد دور ریخته میشود، پس سناریو نمیتواند رتبهٔ دوشنبه را بهجای رتبهٔ امروز نشان بدهد. تنها جایی که این مقدارها ذخیره میشوند ارسالی است که ساعت سکوت تا صبح نگهش داشته: آن پیام مقدارهایی را که با آنها ساخته شده نگه میدارد، چون رویدادی که از آن آمده دیگر خواندنی نیست.
حداکثر ۳۲ ویژگی منتقل میشود، و فقط آنهایی که حداکثر ۵۱۲ بایتاند. آن ۳۲ تا اولینها به ترتیب حروفاند، یعنی در تلاش مجدد هم همان ۳۲ تا. مقدار بلندتر بریده نمیشود بلکه کنار گذاشته میشود، تا بهجای جملهای که وسط راه قطع میشود، یک متغیر بیمقدار باشد.
فقط ویژگیها منتقل میشوند. نام رویداد، زمانش و اطلاعات دستگاهش متغیر نیستند.
کاری که سناریو نمیکند
- روی میزبان مدیریتی هیچ مسیر سناریویی نیست، حتی خواندنی، برخلاف پرچم قابلیتها. و کنترلپلینی که این مسیرها را دارد از اینترنت مسیردهی نشده است. بخش نقاط ورود را ببینید.
- هیچ مسیری جواب نمیدهد «این آدم الان کجای این سناریوست». نقطهٔ ورود گراف فقط آمار تجمعی هر گره را میدهد. خواندن تکنمونه وجود ندارد.
- هیچ مسیری یک نفر را از سناریو بیرون نمیآورد. سازوکارها
exit_criteriaوmax_duration_daysهستند؛ فراخوان «این کاربر را بیرون بینداز» وجود ندارد. - توقفی که آدمهای داخل را متوقف کند وجود ندارد. بخش انتظار را ببینید.
- سناریویی که خودتان ساختهاید جریان تایید ندارد. آن یکی با
journey.publishمنتشر میشود و بس. سناریویی که از کتابخانه یا از پیشنهاد آمده فرق دارد:review_requiredدارد و انتشارش بدون بازبینی خطا برمیگرداند. بازبینی از رفتار سناریو اثر انگشت میگیرد، پس اگر بعد از بازبینی ویرایشش کنید و همان را منتشر کنید هم رد میشود، درست مثل تایید کمپین. - زمانبندی تکرارشوندهٔ خودش را ندارد. نزدیکترین چیز، تریگر جاروب با ساعت است، و ساعتهایش را به وقت تهران میخواند.
- هیچ بررسیای نیست که کانال یک گرهٔ ارسال اصلا قابل تحویل باشد. بخش ارسال از داخل سناریو را ببینید.
offset_daysبعد از تاریخ وجود ندارد. از گرهٔ انتظار استفاده کنید.