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 param | Meaning | Notes |
|---|---|---|
tenant_id | Optional tenant scope | CRM-backed rows need a numeric tenant; notify-backed inspections currently run only when tenant scope is provided |
status | Event status filter | Compared against scheduled, sent, not_sent, or failed |
category | Benefit category filter | Uses inferred campaign primary benefit |
scope | Scope filter | Uses inferred campaign scope |
channel | Channel filter | One of Push, SMS, Mail, InApp |
page | Page number | Defaults to 1 |
page_size | Page size | Defaults 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_inappwhensent_atis present and itsflow_run_idresolves to a released notify-trigger campaignnotify.notifications_webpushwhen its delivery status is supported and itsflow_run_idresolves to a released notify-trigger campaign
Supporting sources:
- CRM
lifecycle_pools,content_pools,bonuses, andcontentfor campaign title, category, and scope resolution NotificationService.notification_statusto refine the latest status for CRM rows keyed bynotification_uuid
Notify rows without a trustworthy Trigger campaign lineage are held back from the public API:
notify.notifications_inappnotify.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:
titletenant_idcategoryscope
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 status | API status |
|---|---|
READY, QUEUED | scheduled |
SENT, DELIVERED | sent |
BLOCKED_BY_COMM_FLAGS, CANCEL_JOURNEY_CHANGE | not_sent |
KAFKA_TIMEOUT, KAFKA_SEND_ERROR, BONUS_ACTIVATION_ERROR | failed |
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 ascrm_action:<actions_log _id>,notify_inapp:<notification _id>, ornotify_webpush:<notification _id>timestamp: source send timestamp when available, then source update/create timechannel: the first entry fromchannels, provided for singular consumerschannels: a single-element array derived from the CRM channel or Notify sourcetitle,category,scope,tenant_id: inherited from the matched CRM or Trigger campaign metadata
Field-by-field meaning
| Field | How it is filled | Source or derivation |
|---|---|---|
id | Stable synthetic event ID | CRM actions-log _id with crm_action: prefix |
tenant_id | Tenant string when known | Matched CRM campaign tenant, else CRM client_id |
timestamp | Best available event timestamp | send_at, then updated_at, then created_at |
title | User-facing title | Matched CRM campaign title |
category | Benefit pillar | Inferred from matched campaign metadata |
scope | Audience/intent label | Inferred from matched campaign metadata |
channel | Notification channel | First entry from channels |
channels | Notification channel | Single-element array derived from the real send record channel |
status | Owner-facing event status | scheduled, sent, not_sent, or failed |
error_message | Human-readable reason when applicable | Derived from blocked or failed status causes |
Applied filters
The response also includes applied_filters, which simply echoes:
tenant_idstatuscategoryscopechannel- normalized
page - normalized
page_size
Truth gaps to keep in mind
flow_runs_v2provides 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_statusis 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.