Broadcast Campaigns
Overview
A campaign is the unit of sending a message out to LINE friends, either immediately or
on a schedule. Three delivery modes are supported through the CastType enum:
| Mode | Meaning |
|---|---|
broadcast | Sent to every friend of the LINE OA |
multicast | Sent to a specific audience segment |
narrowcast | Sent using LINE's own targeting conditions |
The campaign table plays two roles at once: it is both the send instruction and the
delivery report. It stores a snapshot of the content that actually went out, alongside
result counters (reach and clicks) written back from the tracking log.
Core Data Structure
campaign (model Campaign)
Send configuration lives in name, broadcast_now, start_date, cast_type, and
status (enum CampaignStatus).
Key relations:
rich_message_id→rich_message.id— the content to sendaudience_id→audience.id(nullable) — the target segment for multicastline_oa_id→line_oa.idandorganization_id→organization.id
Send-time snapshot, kept so that historical reports remain interpretable even after the source content has been edited:
rich_message_contentJSONB — the resolved message contentline_message_objectJSONB — the exact payload handed to the LINE APItemplate_trackingJSONB — mapping between buttons and tracking keysend_tracking_date— when statistics collection stops
Merge tags — has_merge_tags and skip_merge_tag_missing decide what happens when a
recipient has no value for a variable referenced in the message: skip that recipient, or
send with the placeholder left empty.
Result counters — total_activity, total_recipient, total_reach,
total_unique_click, total_first_click, plus reason for recording why a campaign was
cancelled or failed.
Atomic claim mechanism — the claimed_at column (added via manual SQL) together with
the sending status appended through ALTER TYPE "CampaignStatus" ADD VALUE prevents
multiple workers from picking up the same campaign twice. Rows stuck in sending beyond
the lease window are reverted to scheduled by a reaper process.
campaign_delivery_batch
This table exists only in manual SQL and is not part of the Prisma schema. It tracks delivery split into smaller chunks.
- unique
(campaign_id, batch_no)withuser_countandstatus(pending/sending/done/failed) - per-batch counters
sent_count,error_count,skipped_count - a partial index on
(campaign_id, status)for checking which batches are still open
The flow is: the worker's planner inserts batch rows, delivery workers claim and update them, and the last batch to finish closes out the campaign's aggregate totals.
Related Files
prisma/schema.prisma:380— modelCampaignmanual-sql/8.campaign_claim.sql— thesendingstatus andclaimed_atcolumnmanual-sql/8.campaign_delivery_batch.sql— the batch table (per the worker-go spec)manual-sql/4.sql— thenotify_campaign_clicktrigger ontracking_logseed-data/12.campaign.sql— sample campaigns for all three modesschema-dumps/2026-07-24/schema.sql:1154,:1190
Connections to Other Services
- cms-api-go — creating, editing, and cancelling campaigns, plus the reporting screens
- worker-go — the actual sender: claims campaigns, splits batches, calls the LINE API,
updates counters, and listens on the
campaign_clickchannel - client-api-go — the redirect endpoint that records a
tracking_logentry when a user taps a link, before forwarding to the real destination
Related domains: Message Library (content), Audience (targeting), Click Tracking (results), Event Outbox (click-driven triggers)