Journeys
Journeys are visual graphs that fire when an event happens (or a player enters a segment). Drag-and-drop graph editor (React Flow) with seven node types.
Node types
| Type | Behavior |
|---|---|
| trigger | Entry — references an event name (or segment_entry). Auto-created from the trigger config. |
| wait | Hold for a duration (duration_ms) OR until an event fires (until_event with timeout). |
| send_message | Render template + provider, enqueue a message_send. |
| condition | Branch on a player trait. Operators: =, !=, >, >=, <, <=, exists. Two outgoing edges (if_true_next, if_false_next). |
| grant_bonus | Issue a bonus template. Optional deposit_amount_source for deposit_match types. |
| call_webhook | HTTP POST to an external URL with Mustache-rendered body + optional HMAC signature. Branches on success / failure. |
| end | Terminal — journey run completes. |
Triggers
Three trigger kinds:
- Event trigger —
trigger.event = "deposit_confirmed". Journey fires for every event matching the name in the project. Optional property filter (trigger.where). - Segment entry —
trigger.segment_id = "..."+trigger.kind = "segment_entry". Fires when a player gains membership in the segment. - Manual / API —
POST /v1/journeys/:id/trigger { player_id }for testing or programmatic kickoff.
Graph constraints
- Max 50 nodes per journey
- Every node must have a unique
id start_node_idmust reference an existing node- Cycles are detected at save time (depth limit = 50)
Wait nodes
Two waiting modes:
{
"id": "wait_24h",
"type": "wait",
"duration_ms": 86400000, // wait fixed duration (24h)
"next": "send_welcome"
}
{
"id": "wait_for_deposit",
"type": "wait",
"until_event": {
"event": "deposit_confirmed",
"timeout_ms": 604800000 // 7 days
},
"next": "send_thanks"
}
until_event is the journey's event-listening primitive — wait up to N ms for the player to do event_name; if timeout fires first, follow next anyway.
Webhook nodes (Sprint 18)
{
"id": "anti_fraud_check",
"type": "call_webhook",
"url": "https://anti-fraud.example.com/check",
"method": "POST",
"body_template": "{\"player_id\":\"{{player.id}}\",\"run\":\"{{journey.run_id}}\"}",
"hmac_secret_env": "ANTI_FRAUD_HMAC_SECRET",
"timeout_ms": 3000,
"next_node_id": "grant_bonus_after_check",
"on_failure_next_node_id": "skip_bonus"
}
The webhook firer:
- Renders Mustache body against
{ player, journey }context - Signs with HMAC-SHA256 over
<timestamp>.<body>(matches our inbound webhook scheme — same secret usable on both legs) - Bounded timeout (≤10s)
- 2xx →
next_node_id; non-2xx / timeout / network error →on_failure_next_node_id(or fail journey if not set) https://only — refuses plain HTTP to avoid leaking journey context
Common patterns
- Welcome series: trigger
signup→ wait 24h → send_message email → wait 7d (or untildeposit_confirmed) → grant_bonus. - Win-back: segment_entry "7d inactive" → send_message push → wait 3d → condition (deposit since?) → if not, send_message email with bonus link.
- VIP onboarding: trigger
tier_promoted to platinum→ call_webhook (notify VIP host system) → send_message congrats email. - Risk gate: trigger
deposit_initiated→ call_webhook (anti-fraud) → on_failure: condition (held by anti-fraud?) → grant_bonus only if cleared.