Overview
Tickets emit domain events that are published as behavior events (TICKET_*). Journeys can listen to these events on edge triggers, the same way as custom events.
Ticket journeys use the ticket as their execution subject. Two tickets associated
with the same customer enter and progress independently, and a ticket without a
customer can still complete ticket-safe steps.
Protected event types
Ticket lifecycle events are internal protected behavior events. They can trigger journeys, but they cannot be emitted from Storefront event endpoints or created as custom event types:TICKET_CREATEDTICKET_UPDATEDTICKET_PRIORITY_CHANGEDTICKET_STAGE_CHANGEDTICKET_ASSIGNEE_ADDEDTICKET_ASSIGNEE_REMOVEDTICKET_RESOLVEDTICKET_CLOSEDTICKET_REOPENEDTICKET_COMMENT_ADDED
Recommended pilot journey
- Select Ticket as the journey subject.
- Select the ticket pipeline this journey manages.
- Use
TICKET_CREATEDas the entry trigger. - Add ticket-safe actions: email to an explicit recipient, change ticket stage, compensate customer, or loop.
- Add a New ticket stage condition to
TICKET_STAGE_CHANGED, for example New ticket stage is Waiting Customer. - Use Change ticket stage to move the current ticket to an exact stage from the selected pipeline.
Ticket stage action
The stage action always targets the ticket that owns the journey execution. It does not update every ticket associated with the same customer or source journey. The pipeline is selected once in the journey settings. Every Change ticket stage action then offers only active stages from that pipeline. The journey stores its pipeline id and each action stores a stable stage code, so renaming a stage does not break the flow. Changing the journey pipeline resets its stage actions to the new pipeline’s default stage.Ticket conditions
When aTICKET_* trigger is selected, the condition editor only exposes
the ticket fields available in that event. Order, purchase, RFM, tier, and
unrelated customer conditions are hidden because they cannot be evaluated from
a ticket lifecycle event.
TICKET_CREATED can branch using the ticket data available at creation:
- title and description
- priority, status, pipeline, stage, and source
- customer, assignee, and review presence or identifiers
- brand, channel, and store identifiers
Priority, status, and source use predefined values. Pipeline and stage values
are loaded from the account, so branches do not require internal codes.
Related entity assignees
Ticket journey templates can use the responsible people assigned to the ticket’s store, brand, campaigns, and journeys. Select the entity in the variable picker, then use one of these fields:
For example,
{{store.assignees}} renders the store’s responsible people and
{{brand.assignee_emails}} can be used as the recipient of a ticket email.
Each address receives its own email. If nobody is assigned, that send is
skipped. The recipient field only accepts email variables and warns when the
chosen variable is not an email.
Campaign and journey variables combine the responsible people from every
matching association on the ticket and remove duplicates.
Only active profiles that still belong to the ticket’s account are included.
Values are captured when the ticket event is emitted, so a journey execution
uses the assignments stored in that event.
Event delivery
Ticket lifecycle changes publish protected behavior events (TICKET_*) directly to the events table and enqueue them for journeys. No separate outbox table or custom event type rows are required.
The journey queue does not derive routing from a ticket’s existing journey
association. Every active ticket journey with a matching trigger can evaluate
the event when the ticket belongs to that journey’s configured pipeline.
Compensate a customer
Use Compensate customer after a ticket condition that qualifies for service recovery, such as an urgent ticket or a specific resolution stage. Configure the reward and policy in the action dialog. This action is available in ticket and customer journeys. Ticket journeys use the customer currently associated with the ticket. Customer journeys use the customer executing the step, without requiring a ticket. For example, branch on an urgent ticket, issue one discount valid for 30 days, and then continue to the next ticket stage. Keep the default account scope and 720-hour cooldown to prevent a second ticket from issuing another compensation within 30 days. Select Only once, ever to block future compensations entirely.
Defaults: one reward, 30-day validity, account-wide 30-day cooldown, no additional
caps, and skip if the reward is already available. Caps count issuance operations,
not reward units. All configured limits apply together. They count successful
customer and ticket journey compensations; other campaigns do not consume these caps. Changing a
policy applies its new settings to the existing issuance history.
A ticket receives at most one compensation across all journeys, including after
reopening, loops, retries or a change in its customer association. Tickets with
no customer skip this action and continue. Reaching a customer limit also skips
the action without delaying or closing the ticket. Skipped actions are not
scheduled for another attempt when their cooldown ends.
Customer journeys deduplicate each action within a step. Retrying the same step
does not issue another reward; a new entry can issue one if the policy allows it.
The scheduled executor processes nodes containing compensation, including nodes
that also contain in-app messages.
The wallet and its normal
ACCUMULATION transaction are committed together with
a durable ticket_compensations record. Redeeming or expiring the reward does not
reset the history. The transaction records TICKET_COMPENSATION or
JOURNEY_COMPENSATION as its event type and includes ticket_id, journey_id,
node_id, step_id and action_index in metadata. No campaign is
created. Gift cards use the existing code inventory; insufficient stock fails
atomically without issuing a reward or consuming a customer limit.
Missing or invalid rewards, ambiguous customer associations, and database errors
are execution failures, not policy skips. A failed commit leaves the action
available for the executor to retry. Concurrent executions serialize decisions
for the same account/customer, and wallet version conflicts reload the wallet
before retrying. This protects balances modified by other loyalty operations.
Applying the database change
After the original ticket compensation migration, apply20260929160000_extend_journey_compensation.sql. This incremental migration
preserves existing grants and adds the service-role-only
commit_journey_compensation RPC. It retains the previous RPC during deployment.
Then run pnpm gen:db-types:local or pnpm gen:db-types against the updated
database. The ledger is operational history; reward reporting continues through
normal transactions.