Skip to main content

GET /api/v1/campaigns

Purpose

GET /api/v1/campaigns returns the paginated campaign list used by the operations page, plus list-level summary counters and status buckets.

The response has four top-level blocks:

  • campaigns
  • summary
  • status_counts
  • applied_filters

Query parameters

Query paramMeaningNotes
tenant_idOptional tenant scopeApplied where tenant data is available
searchFree-text searchMatches title, description, scope, campaign_origin, and tags
statusUI lifecycle filterCompared against ui_status, not against the collapsed status field
benefitBenefit pillar filterOne of Conversion, Monetization, Retention
typeCampaign type filterOne of General, Seasonal, Sports, Promo, Acquisition; CRM bonus rows match any associated lifecycle type
channelNotification channel filterOne of Push, SMS, Mail, InApp
pagePage numberDefaults to 1
page_sizePage sizeDefaults to 20, max 200

Data sources

The list endpoint does not have its own bespoke read model. It reuses the same merged campaign record pipeline as the overview endpoint:

  • CRM lifecycle_pools
  • CRM content_pools
  • CRM bonuses
  • CRM content
  • CRM reward_log
  • CRM actions_log
  • Notify Triggers flows_v2
  • Notify Triggers flow_versions_v2
  • Notify Triggers flow_runs_v2
  • Notify Triggers flow_working_copies_v2
  • Notify Triggers flow_release_attempts_v2
  • Spark Bonus dispatches
  • Lumio conversations for the summary.support_cases_total field

How filtering works

Base campaign set

The repository loads every CRM-backed and trigger-backed campaign record for the tenant scope.

Base filters

Before status is considered, the endpoint applies:

  • tenant_id
  • search
  • benefit
  • type
  • channel

This filtered set is used to compute status_counts.

Status filter

If status is provided, it is applied against ui_status, not against the response field status.

This matters because:

  • ui_status can be suggestion
  • status only returns running, paused, or ended
  • rows with ui_status = suggestion return status = paused

So status=suggestion can return rows whose response status field is paused.

Top-level response blocks

BlockWhere it comes fromImportant caveat
campaignsPaginated campaign rows after all filtersSorted by ui_status, then end date, then start date, then title
summaryOverview counters rebuilt from the filtered campaign rowssupport_cases_total is not filtered by search, status, benefit, type, or channel
status_countsCounts by ui_status on the pre-status-filtered setBuckets include suggestions, running, paused, ended
applied_filtersEcho of normalized query values from the service layerpage and page_size are normalized defaults/max values

Campaign row field map

FieldHow it is filledSource or derivation
idStable synthetic IDcrm_bonus:<bonus_id> for CRM rows, crm_reactive:<flow_id> for trigger rows
bonus_idCRM bonus identityPresent only for bonus-centric CRM rows
tenant_idTenant string when knownCRM client_id; trigger-side only when Spark Bonus dispatches point to exactly one client_id
titleUser-facing campaign nameExact CRM bonus title; trigger flow name
statusCollapsed lifecycle statusrunning and ended stay as-is; everything else collapses to paused
typeCompatibility campaign typeOne associated type, or General when a CRM bonus spans several lifecycle types
typesAll campaign typesAdditive CRM metadata derived from every lifecycle association
benefitsUI benefit pillarsHardcoded to Conversion, Monetization, Retention
scopeCompatibility audience labelOne associated scope, or Multiple audiences when a CRM bonus spans several scopes
scopesAll audience labelsAdditive CRM metadata derived from every lifecycle association
channelsPossible delivery channelsCRM channels_templates; trigger notification action refs
tagsSearch-friendly synthetic tagsCRM bonus rows include all associated types and scopes; trigger rows use type, benefit, scope, and origin
image_urlCampaign image when availableCRM content placeholders; currently empty for trigger rows
benefit_scoresUI benefit score blockHardcoded to 12, 6, 2
start_atCampaign start timeExact CRM bonus campaign start; trigger earliest successful notification execution
end_atCampaign end timeExact CRM bonus campaign end; trigger latest successful notification execution
campaign_originUpstream source labelcrm or crm_reactive
ui_statusUI-facing lifecycle bucketsuggestion, running, paused, or ended
availability_statusDate-based bonus availabilityAdditive CRM value: active, future, ended, or unknown
route_coverageLifecycle readiness summaryAdditive CRM {ready,total,state} object; state is none, partial, or full

CRM rows are grouped by bonus ID. A bonus referenced by several lifecycle content pools appears once, while its lifecycle types, scopes, channels, readiness, and activity are aggregated into that row.

Status derivation

CRM rows

CRM ui_status is inferred once per unique bonus from:

  • whether any lifecycle association is ready
  • start_at
  • end_at

The endpoint uses the Any-active plus coverage rule:

  • suggestion when ready = false
  • ended when at least one route is ready but end_at is in the past
  • paused when at least one route is ready but start_at is in the future
  • running when at least one route is ready and the bonus is inside its date window, or when its dates are unspecified

Recent reward or action activity contributes metrics but does not downgrade a usable bonus from running. route_coverage independently reports how many associations are ready and whether coverage is none, partial, or full.

Trigger rows

Trigger ui_status is inferred from:

  • enabled
  • whether successful notification actions were seen
  • whether successful Spark Bonus activations were seen
  • whether the working copy is newer than the last successful release

Current logic:

  • running when the flow is enabled
  • suggestion when the flow is disabled and has no sent or activated activity
  • paused when the working copy is newer than the last successful release
  • ended otherwise

Summary block caveat

summary.support_cases_total is counted from Lumio chat conversations using tenant scope only. It does not respect:

  • search
  • status
  • benefit
  • type
  • channel

That field is therefore best read as "tenant-level Lumio chat volume alongside this list view", not "support cases caused by the filtered campaigns".

Truth gaps to keep in mind

  • benefits and benefit_scores are currently UI placeholders, not measured campaign outcomes.
  • status is a simplified field. Use ui_status when the UI needs the real suggestion bucket.
  • CRM type and scope remain singular compatibility fields. Use the additive types and scopes arrays when all associated values are needed.