Journey'ler (Faz 11)
Journey, kullanıcıyı bir tetikleyiciyle içeri alıp bekleme, koşul, dallanma ve gönderim adımlarından geçiren otomatik akıştır (OneSignal Journeys eşdeğeri). Tasarım kararları: ADR-0018. Şema ve doğrulama:…
Journey, kullanıcıyı bir tetikleyiciyle içeri alıp bekleme, koşul, dallanma ve gönderim adımlarından geçiren otomatik akıştır (OneSignal Journeys eşdeğeri). Tasarım kararları: docs/adr/0018-journey-motoru.md. Şema ve doğrulama: packages/shared/src/journeys.ts.
Yaşam döngüsü
| Durum | Anlamı |
|---|---|
draft | Hiç yayınlanmadı; silinebilir. |
active | Yayında; yeni girişler kabul edilir, run'lar ilerler. |
paused | Yeni giriş yok, bekleyen run'lar bekletilir; resume ile devam eder. |
archived | Kalıcı kapalı; açık run'lar journey_archived nedeniyle çıkarılır. |
Düzenleme her zaman taslak sürüm üzerinde yapılır (PUT …/draft, PATCH …/nodes/:nodeId). publish taslağı yeni bir sürüm olarak yayınlar. Aktif run'lar girdikleri sürümde kalır (sürüm sabitleme); yeni girişler en son sürümü kullanır. Yayınlamadan önce yapısal ve anlamsal doğrulama çalışır; sorun varsa 400 ve details.issues döner.
Graf biçimi
React Flow uyumludur:
{
"nodes": [{ "id": "t", "type": "trigger", "position": { "x": 0, "y": 0 }, "data": { "trigger": { "kind": "event", "event_name": "signup" } } }],
"edges": [{ "id": "e1", "source": "t", "target": "w", "sourceHandle": "next" }]
}Kurallar:
- Tam olarak bir
triggerdüğümü olur ve döngü kurulamaz. - Her düğüm tetikleyiciden erişilebilir olmalıdır.
- Her çıkış kolu (handle) en fazla bir kenara bağlanır; bağlanmamış kol, run'ın o noktada
completedolması demektir. - Sınırlar
JOURNEY_LIMITSiçindedir: en fazla 100 düğüm, 200 kenar ve 5 dal; bekleme 60 sn ile 365 gün arasıdır.
Düğüm türleri
| Tür | Veri | Kollar |
|---|---|---|
trigger | trigger.kind: event (+property_filters), segment_enter, api, schedule (segment_id, 5 alanlı cron, timezone) | next |
wait | wait.mode: duration (seconds), until_time (HH:MM, user/app saat dilimi), until_date (at) | next |
wait_until | conditions[]: event, segment_enter, message (node_id + olay) ve timeout_seconds | cond_0…cond_n, timeout |
time_window | windows[] (days 0=Pazar…6, start, end) ve saat dilimi; pencere dışındaysa bir sonraki pencereye kadar bekler | next |
condition | rule: segment, filters (segment AST), message (yukarı akıştaki gönderimin olayı), event (within_days) | yes, no |
split | branches[] (id, percent; toplam 100). Atama run_id üzerinden deterministiktir (FNV-1a) | dal kimlikleri |
send_push / send_email / send_sms | template_id veya satır içi content | next |
send_in_app | in_app_message_id, expires_seconds | next |
webhook | url, headers, Liquid body (yalnızca POST) | next |
update_tags | tags (null değeri tag'i siler) | next |
exit | — | — |
Mesaj olayları: sent, delivered, opened, clicked, failed, bounced, complained, unsubscribed.
Push → SMS yedeği örneği: send_push → wait_until [message (push düğümü, delivered), zaman aşımı 1 saat] → timeout kolu → send_sms.
Gönderim adımları
- Her gönderim,
source = journeyvejourney_run_idalanlarıyla birmessageskaydı oluşturur. Gönderim normal teslimat hattından geçer, kampanya analitiği de bu kayıtla çalışır. - Kullanıcının o kanalda aktif aboneliği yoksa adım
skippedolur ve run devam eder. - Sessiz saatler ve frekans sınırı uygulanır;
settings.quiet_hours_exemptvefrequency_cap_exemptile muaf tutulabilir. send_in_app, kullanıcıya süreli bir gösterim hakkı (in_app_journey_grants) verir.journey_only: trueolan in-app mesajlar yalnızca bu hakkı olan kullanıcılara gösterilir.webhookdüğümünün gövdesi Liquid ile yazılır; kapsamdauser,journey,runvecontext(giriş bağlamı) bulunur.- Gövde verilmezse varsayılan JSON gövde gönderilir:
{type: "journey.webhook", journey_id, node_id, run_id, user, context}. - Her istek
idempotency-key: <run_id>:<node_id>başlığını taşır. - SSRF koruması uygulanır.
- Başarısız istek run'ı durdurmaz; sonuç adım istatistiğine yazılır.
- Gövde verilmezse varsayılan JSON gövde gönderilir:
Ayarlar (settings)
| Alan | Açıklama |
|---|---|
re_entry.mode | never (varsayılan), after_exit, after_days (+days) |
only_new_users | Segment tetiklerinde yalnızca yayından sonra segmente girenler |
exit_rules[] | event, segment_enter, segment_exit, unsubscribed, became_active |
exit_tags | Çıkışta (kural, hedef, exit düğümü) yazılacak tag'ler |
goals[], exit_on_goal | Hedef olayı/segmenti; dönüşüm oranı istatistiği |
schedule.start_at / end_at | Yeni girişlerin kabul edildiği aralık |
quiet_hours_exempt, frequency_cap_exempt | Gönderim kısıtlarından muafiyet |
Yürütme ve güvenilirlik
- Doğruluk kaynağı veritabanıdır. Her adım
step_seqüzerinde iyimser kilitle sahiplenilir; aynı işin iki kez çalışması etkisizdir. - Bekleyen run'lar
next_action_atile gecikmeli BullMQ işine bağlanır. - Kurtarma: her 10 sn'de vadesi gelmiş run'lar taranır. Worker yeniden başlasa veya Redis işleri kaybolsa da run'lar devam eder.
- Segment ve takvim tetikleri: Redis kilidi altında her 60 sn'de taranır.
- Sinyaller: olaylar (
/v1/.../events) tetik, çıkış kuralı, hedef vewait_untilkoşullarını işler. Teslimat olayları da (delivered, clicked …) run'lara sinyal olarak gelir. - Webhook olayları:
journey.entered,journey.step_completed,journey.completed,journey.exited(webhooks.md). - Saklama: biten run'lar teslimat saklama süresi (
deliveries) dolunca silinir; süresi geçmiş in-app hakları da temizlenir. Kullanıcı silindiğinde kullanıcının run'ları ve hakları da silinir.
API
Dashboard (oturum) uçları internal/v1/apps/:appId/journeys/… altındadır. Public uçlar aynı yolları v1/apps/:appId/journeys/… altında sunar.
| Yöntem | Yol | Kapsam |
|---|---|---|
| GET | /journeys, /journeys/:id, /journeys/:id/versions, /journeys/:id/stats, /journeys/:id/runs, /journeys/:id/runs/:runId | analytics:read |
| POST/PATCH/DELETE | /journeys, /journeys/:id, PUT /journeys/:id/draft, PATCH /journeys/:id/nodes/:nodeId | admin |
| POST | /journeys/:id/publish, pause, resume, archive, duplicate | admin |
| POST | /journeys/:id/runs (API ile giriş: external_id / user_id / alias, context) | notifications:write |
| DELETE | /journeys/:id/runs/:runId (run'dan çıkar) | admin |
| GET | /users/by/:label/:value/journeys | analytics:read |
Run tetikleme yanıtı {entered, run, reason} biçimindedir. Giriş reddedilirse reason şu değerlerden biri olur: inactive, outside_schedule, re_entry, already_active, unknown_user.
SDK: Node hs.journeys.*, PHP $client->journeys() (sdk/node.md, sdk/php.md).
Örnek: kayıt → satın alma hatırlatma
trigger(event: signup) → wait(1 gün) → condition(event: purchase):
yeskolu:exitnokolu:send_push→wait(3 gün)→send_email
Bu akış, zaman hızlandırmalı kabul testinde doğrulanır (apps/worker/src/__tests__/journeys.int.test.ts).
E-posta, SMS, in-app mesajlar ve bildirim kutusu
Faz 10 ile Pushizo push dışındaki kanalları da gönderir: e-posta (SMTP, Amazon SES, Resend), SMS (Twilio, Netgsm), uygulama içi in-app mesajlar ve bildirim kutusu (inbox). Bu belge kurulumu, API sözleşmesini ve…
A/B testi ve holdout (Faz 12)
Kampanya ve journey gönderimlerinde içerik varyantlarını karşılaştırma, kontrol (holdout) grubu ile artımlı etkiyi ölçme ve kazananı otomatik/elle seçme. Tasarım kararları: ADR-0019. Ortak kod:…