Futura campaign go-live testing
Use this runbook before launching a Futura WhatsApp campaign. The flow depends on typed segment definitions, a materialized recipient snapshot, template personalization, normalized button replies, consent handling, and staffed manual follow-up for interested replies.The durable BORD-433/BORD-441 launch package lives in
planning/campaigns/futura-launch-runbook.md, planning/campaigns/futura-dry-run-fixtures.json, planning/futura/futura-strategy-templates-compliance.md, and planning/campaigns/bord-431-campaign-journey-scope note.md.Scope
The just-merged campaign flow supports:- audience definitions with
all,tags, orsegmenttargeting; - typed custom-field filters for segment audiences;
- recipient materialization into
campaign_recipientsbefore dispatch; - template binding by approved template name or template ID, locale, and body variables;
- normalized button reply IDs for campaign and journey routing;
- campaign-button opt-out events written to the consent ledger;
- deterministic route actions for contact tags, contact attributes, open conversations, and opt-outs.
start_journey execution.
Campaign metadata contract
Futura campaign drafts should carry the v1 campaign metadata alongside the legacy campaign columns. The metadata is the go-live contract operators review before materialization.Preflight checklist
1
Confirm the audience source
Choose
tags for the initial Futura list or segment when the campaign depends on contact attributes. Avoid all unless the workspace explicitly approved a full-list send.2
Validate typed segment filters
For
segment audiences, check every filter before materialization. Supported fields are tags, name, phone, and custom_fields. Custom fields use key=value syntax; gt and lt comparisons must compare number or date values, either from custom field definitions or values that parse as numbers or ISO-style dates.3
Preview the segment count
Preview the segment in the app or via the same backend path used by
previewSegment. Confirm the count and a sample of contacts against the campaign brief.4
Check consent before snapshotting
The materializer skips contacts whose
consent_state is opted_out or whose legacy subscriber_status is unsubscribed. Confirm the expected opt-out count with the campaign owner before continuing.5
Verify the template binding
Confirm the WhatsApp template is approved in Meta and in Switchbord, the
locale matches the approved language code, every body variable has a source plus fallback, body value propositions use WhatsApp bolding, and the opt-out quick reply is visible as Stop promozioni or Non scrivermi rather than STOP.6
Freeze routes and safety limits
Confirm
responseRoutes only uses the supported button IDs: futura_not_booked, futura_already_booked, and marketing_opt_out. Confirm route actions do not include start_journey for MVP launch. Confirm launchSafety.maxRecipients is at or below the approved send size.Materialize recipients
Materialization turns the audience definition into durablecampaign_recipients rows. Treat this as the audience snapshot for the send.
Expected behavior:
- reads the campaign
audienceDefinitionfrom v1 metadata; - resolves
all, tag, or segment audiences within the workspace; - deduplicates contacts by ID;
- skips opted-out or unsubscribed contacts;
- inserts new
queuedrecipients with a uniquecampaign_id,contact_idkey; - updates campaign metadata with
materialization.materializedCount,skippedOptOutCount,duplicateCount, andmaterializedAt.
materializedCountmatches the approved count after consent skips;skippedOptOutCountis explainable and nonzero only when expected;- a second materialization run reports duplicates instead of creating duplicate recipients;
- no
campaign_recipientsrows were created for contacts outside the workspace; - no rows exist for known opted-out test contacts.
Dispatch and personalization checks
Campaign dispatch only processes recipients inqueued status while the campaign is running. It sends in batches and requeues campaign.dispatch until no queued recipients remain.
For each test recipient, verify:
- a conversation exists or is created in the expected channel;
- an outbound
messagesrow is queued withsend_mode: templateandauthor_label: Campaign; payload.templateNameis the bound template name;payload.templateLocaleis present when a locale is configured;payload.templateComponentsincludes body parameters in index order;contact.first_nameresolves fromcontacts.metadata.first_namefirst, then from the first token ofcontacts.name, then to the configured fallback;- the rendered message body preview replaces
{{1}},{{2}}, and later placeholders consistently with the template components; - opted-out recipients encountered at dispatch are marked failed with
error_code: skipped_opt_outand are not sent.
Recipient materialization skips known opt-outs up front, but dispatch rechecks consent so contacts who opt out between materialization and send are still blocked.
Button reply normalization
Inbound webhook processing normalizes both WhatsApp button formats into a single button ID before route handling:- legacy reply buttons:
message.button.payload; - interactive buttons:
message.interactive.button_reply.id; - interactive list replies:
message.interactive.list_reply.id.
Reject route definitions that use button labels, translated titles, spaces, or casing variants as keys. Routes must use normalized IDs, not display text.
Consent opt-out test
For themarketing_opt_out button:
- Send a test campaign message to a consenting seed contact.
- Reply by tapping the opt-out button, not by typing a STOP keyword.
- Confirm a
consent_eventsrow is written withevent_type: opted_out,source: campaign_button,actor: system, and metadata containing thebuttonIdand provider message ID. - Confirm the contact
subscriber_statusis updated tounsubscribedand the contact is no longer eligible for subsequent campaign materialization. - Confirm later dispatch attempts skip the contact before creating a Meta send.
Manual follow-up instead of campaign-started journeys
BORD-431 campaign responsestart_journey execution is scope noted from the MVP launch. A route action with type: "start_journey" is accepted by the domain schema but is not executed as a campaign-to-journey attachment/start path. Operators must not depend on it for Futura launch follow-up.
Use this manual follow-up path:
futura_not_bookedtags the contact, recordsfutura_response=not_booked, and opens/routes the conversation to sales;- a human operator reviews the conversation and sends the next approved reply manually;
futura_already_bookedtags or marks the contact as already booked and requires no automatic journey;marketing_opt_outrecords consent opt-out and must never trigger a marketing follow-up.
Operational go-live checklist
Before production launch:- Campaign metadata validates against v1 schema.
- Segment preview or tag count matches the signed-off audience.
- Typed custom-field filters were tested with at least one matching and one non-matching seed contact.
- Recipient materialization counts were recorded in launch notes.
- Approved template name, locale, and body-variable fallbacks were verified.
- Template component payloads were inspected for at least two seed contacts.
- Button IDs were tested from WhatsApp, not only from mocked webhook payloads.
-
marketing_opt_outcreated acampaign_buttonconsent ledger event. - Post-opt-out materialization and dispatch both skipped the seed contact.
- Manual follow-up staffing is assigned for
futura_not_bookedand any optionalfutura_already_bookedreplies. - Production route metadata contains no
start_journeyactions. -
campaign.dispatchandmessage.dispatchoutbox jobs drained without unexpected failures. - Delivery, read, failed, and skipped counts were compared with campaign detail metrics.
- Incident owner and rollback/pause decision point are documented.
Rollback and pause criteria
Pause the campaign and stop queue processing if any of these happen:- materialization includes contacts outside the approved audience;
- opt-out contacts receive campaign messages;
- button replies are recorded as display labels instead of normalized IDs;
- launch planning depends on automatic campaign-to-journey start before BORD-431 is implemented;
- Meta returns sustained policy or template errors for the Futura template;
- delivery failures exceed the campaign owner’s agreed threshold.