Skip to main content

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_CREATED
  • TICKET_UPDATED
  • TICKET_PRIORITY_CHANGED
  • TICKET_STAGE_CHANGED
  • TICKET_ASSIGNEE_ADDED
  • TICKET_ASSIGNEE_REMOVED
  • TICKET_RESOLVED
  • TICKET_CLOSED
  • TICKET_REOPENED
  • TICKET_COMMENT_ADDED
  1. Select Ticket as the journey subject.
  2. Select the ticket pipeline this journey manages.
  3. Use TICKET_CREATED as the entry trigger.
  4. Add ticket-safe actions: email to an explicit recipient, change ticket stage, compensate customer, or loop.
  5. Add a New ticket stage condition to TICKET_STAGE_CHANGED, for example New ticket stage is Waiting Customer.
  6. Use Change ticket stage to move the current ticket to an exact stage from the selected pipeline.
Actions that require a customer profile, such as push, in-app, WhatsApp, destination, Braze, and Meta actions, are not available in ticket journeys. Ticket email requires an explicit recipient address and does not depend on a customer association.

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 a TICKET_* 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
Lifecycle-specific events add their transition data: Priority, status, and source use predefined values. Pipeline and stage values are loaded from the account, so branches do not require internal codes. 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, apply 20260929160000_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.