Skip to main content

GET /api/v1/live-events

Purpose

GET /api/v1/live-events returns the paginated live-event feed for notification end actions. It is meant to show actual player-facing send records, not Spark Bonus award dispatches or trigger orchestration traces.

Query parameters

Query paramMeaningNotes
tenant_idOptional tenant scopeCRM-backed rows need a numeric tenant; notify-backed inspections currently run only when tenant scope is provided
statusEvent status filterCompared against scheduled, sent, not_sent, or failed
categoryBenefit category filterUses inferred campaign primary benefit
scopeScope filterUses inferred campaign scope
channelChannel filterOne of Push, SMS, Mail, InApp
pagePage numberDefaults to 1
page_sizePage sizeDefaults to 20, max 200

Data sources

The live-event feed is built from actual send ledgers, not from flow execution rows.

API-visible sources:

  • CRM actions_log
  • notify.notifications_inapp when sent_at is present and its flow_run_id resolves to a released notify-trigger campaign
  • notify.notifications_webpush when its delivery status is supported and its flow_run_id resolves to a released notify-trigger campaign

Supporting sources:

  • CRM lifecycle_pools, content_pools, bonuses, and content for campaign title, category, and scope resolution
  • NotificationService.notification_status to refine the latest status for CRM rows keyed by notification_uuid

Notify rows without a trustworthy Trigger campaign lineage are held back from the public API:

  • notify.notifications_inapp
  • notify.notifications_webpush

Explicitly excluded as live-event row sources:

  • notify_triggers.flow_runs_v2
  • Spark Bonus dispatches
  • Lumio conversations

How rows are created

1. Rebuild CRM campaign metadata

The repository first rebuilds CRM campaign context so each live event can inherit:

  • title
  • tenant_id
  • category
  • scope

2. Read actual send rows

CRM actions_log rows and actual Notify sends are treated as candidate live events. Rows created by the current CRM awarder carry campaign_content_pool_id, which is the authoritative CRM campaign identity. Historical CRM rows without this field use the legacy bonus-placeholder relationships already used by the campaign aggregate path. Notify rows use their persisted flow_run_id, which resolves through notify_triggers.flow_runs_v2 to the released flow campaign.

3. Resolve only rows with a safe campaign title

CRM rows with campaign_content_pool_id are API-eligible only when it maps to exactly one CRM campaign record. Historical CRM rows without it may use the legacy bonus-placeholder mapping. Notify rows are API-eligible only when flow_run_id maps to a released Trigger campaign for the tenant. If any path finds no safe campaign, or the CRM legacy path finds multiple campaigns, the row remains internal and is not returned by the API.

4. Normalize owner-facing status

Source statusAPI status
READY, QUEUEDscheduled
SENT, DELIVEREDsent
BLOCKED_BY_COMM_FLAGS, CANCEL_JOURNEY_CHANGEnot_sent
KAFKA_TIMEOUT, KAFKA_SEND_ERROR, BONUS_ACTIVATION_ERRORfailed

When a CRM row is still only scheduled, the repository may refine it with the latest NotificationService.notification_status row for the same notification_uuid. That stream is enrichment only; it does not create the business event row by itself.

5. Build one event row per actual send record

Each row uses:

  • id: a stable source-specific ID such as crm_action:<actions_log _id>, notify_inapp:<notification _id>, or notify_webpush:<notification _id>
  • timestamp: source send timestamp when available, then source update/create time
  • channel: the first entry from channels, provided for singular consumers
  • channels: a single-element array derived from the CRM channel or Notify source
  • title, category, scope, tenant_id: inherited from the matched CRM or Trigger campaign metadata

Field-by-field meaning

FieldHow it is filledSource or derivation
idStable synthetic event IDCRM actions-log _id with crm_action: prefix
tenant_idTenant string when knownMatched CRM campaign tenant, else CRM client_id
timestampBest available event timestampsend_at, then updated_at, then created_at
titleUser-facing titleMatched CRM campaign title
categoryBenefit pillarInferred from matched campaign metadata
scopeAudience/intent labelInferred from matched campaign metadata
channelNotification channelFirst entry from channels
channelsNotification channelSingle-element array derived from the real send record channel
statusOwner-facing event statusscheduled, sent, not_sent, or failed
error_messageHuman-readable reason when applicableDerived from blocked or failed status causes

Applied filters

The response also includes applied_filters, which simply echoes:

  • tenant_id
  • status
  • category
  • scope
  • channel
  • normalized page
  • normalized page_size

Truth gaps to keep in mind

  • flow_runs_v2 provides campaign lineage for Notify records; it is not itself a live-event row source. A trigger flow can exist without a public event until it produces an actual in-app or web-push send.
  • Notify records without flow_run_id, or whose run/flow cannot be resolved to a released campaign, stay internal rather than being attributed from message content.
  • NotificationService.notification_status is an append-like status stream. It is used only to refine already-created CRM live events.
  • Spark Bonus dispatches help campaign aggregates, but they are not rendered as live-event rows themselves.
  • New CRM action rows persist an explicit content-pool identity. Legacy CRM rows with an ambiguous campaign mapping remain excluded until they can be reconciled safely.