title: Campaign Send Flow description: How a scheduled campaign expands its segment into per-contact sends and dispatches through Novu for email and SMS delivery.
Campaign Send Flow
A scheduled campaign is picked up by a cron-driven endpoint, its target segment is expanded into per-contact sends, and each send is dispatched through Novu (which fronts email and SMS providers in this stack). There is no separate bounce webhook today — delivery status is whatever Novu reports back when the workflow triggers, and success/failure is recorded on the CampaignSend row.
Step explanations.
1-3. GET /api/cron/campaigns is the only documented trigger today. It is authenticated by the x-api-key header matching CRON_SECRET; the handler rejects otherwise and defers all work to processScheduledCampaigns, which just selects campaigns with status=scheduled and scheduledAt <= now.
4-8. sendCampaign dispatches by providerType (email or sms). Both branches load the Campaign, its Survey, and the target Segment; both resolve segment membership via getContactsInSegment before touching any provider.
9-14 (email). The email branch builds the HTML once (getPreviewEmailTemplateHtml) and registers a Novu workflow per campaign (createEmailWorkflow). Contacts are processed in batches of five, with a CampaignSend row written per recipient and updated to sent/failed based on whether triggerWorkflow returns.
15-20 (SMS). The SMS branch creates a HiveCFM Response row up front (finished: false) so that later DTMF/SMS answers can attach to it, seeds an SMS session keyed by phone, and sends the first question through sendSmsMessage. Subsequent answers arrive through a separate SMS inbound path (not shown here).
- After the per-contact loop the campaign row is marked
sent(orfailedif every send failed).
Bounce handling: there is no dedicated bounce webhook endpoint in hivecfm-core today. The CampaignSend.status field is the source of truth, and it is populated from the success/failure of the synchronous Novu call — not from a provider callback. Asynchronous bounces (SES complaints, carrier rejections) are not currently reconciled.
Where to look in the code
hivecfm-core/apps/web/app/api/cron/campaigns/route.ts— cron entrypoint, requiresx-api-key = CRON_SECRET.hivecfm-core/apps/web/lib/campaign/campaign-scheduler.ts—processScheduledCampaignsloop.hivecfm-core/apps/web/lib/campaign/send-campaign.ts—sendCampaigndispatcher and the email / SMS branches.hivecfm-core/apps/web/lib/novu/service.ts—createEmailWorkflow,triggerWorkflow,sendSmsMessage.hivecfm-core/apps/web/lib/campaign/sms-session.ts— in-memory session map used by the SMS answer path.