diff --git a/docs/trucking-state-campaign-send-runbook.md b/docs/trucking-state-campaign-send-runbook.md new file mode 100644 index 0000000..918562e --- /dev/null +++ b/docs/trucking-state-campaign-send-runbook.md @@ -0,0 +1,96 @@ +# Trucking State-Campaign Send Runbook + +Companion to `trucking-marketing-send-plan.md`. The plan ranks the sends; this +runbook is the exact, repeatable procedure to launch them. Everything up to the +final "start the send" step is safe and idempotent. **Starting a bulk send is +irreversible** and is the only step that should be run deliberately by an +operator. + +## Prerequisites + +- `DATABASE_URL` pointing at production Postgres (FMCSA census + compliance_orders). +- `LISTMONK_URL`, `LISTMONK_USER`, `LISTMONK_PASS` for the Listmonk instance. +- Migrations applied through at least `083_fmcsa_campaign_tracking.sql` + (`listmonk_campaign_type`, `listmonk_sent_at`) and, for fulfillment status, + `086_state_trucking_fulfillment_status.sql`. + +## Prioritized send order + +Lowest fulfillment friction first (see the plan for the rationale): + +1. **NY HUT setup** — clean tax-pro authorization (E-ZRep / TR-2000). +2. **CT Highway Use Fee** — electronic via myconneCT, CSV vehicle import. +3. **DOT Drug & Alcohol Program** — no state portal; recurring. +4. **New Carrier Startup Bundle** — broad cross-sell; data-targeted. +5. **CA MCP + CARB** — medium friction (portal + insurance coordination). + +## Step 1 — Build/refresh the audience lists + +The draft campaigns and their lists are created by: + +```sh +LISTMONK_URL=… LISTMONK_USER=api LISTMONK_PASS=… \ + python3 scripts/setup_trucking_campaigns.py +``` + +This is **safe**: it creates draft campaigns (NY HUT, CT HUF, D&A, CA MCP, New +Carrier Startup, hazmat) and sends a single test to `CAMPAIGN_TEST_EMAIL`. It +does not start any bulk send. + +For the New Carrier Startup list specifically, populate it with the +data-targeted, billed-at-cost-aware audience (carriers with usable email, recent +FMCSA `add_date`, small fleet, and no paid Performance West startup order): + +```sh +# Preview first — read-only, prints sample candidates and a count. +DATABASE_URL=… python3 scripts/populate_new_carrier_startup_campaign.py --dry-run + +# Then import (does NOT mark listmonk_sent_at; adding to a list != sending). +DATABASE_URL=… LISTMONK_URL=… LISTMONK_PASS=… \ + python3 scripts/populate_new_carrier_startup_campaign.py --limit 500 --recent-days 180 +``` + +Subscriber attributes set: `company`, `dot_number`, `state`, `city`, `trucks`, +`drivers`, `add_date`, `missing_items_html`, `startup_score`. + +State lists (NY/CT/CA) are populated from the FMCSA census by base/operating +state. Use the state-segment tooling to attach carriers by `phy_state` / +operating-state to each list before sending. + +## Step 2 — Review the draft + test send + +In Listmonk, open each draft campaign and: + +- confirm the subject and `missing_items_html` / company merge tags render; +- confirm the list count is the audience you expect; +- confirm the test email looks correct in a real inbox (rendering, links, CTA). + +## Step 3 — Start the send (operator action, irreversible) + +Send one campaign at a time, in the priority order above, and watch +deliverability/bounces before starting the next. In Listmonk, set the campaign +status to `running` (or `scheduled` with a send time). + +For the scheduled MCS-150 / inactive-USDOT regional sends, the nightly builder +`build_trucking_campaigns.py` already schedules per timezone; the state campaigns +here are launched manually so each can be paced. + +After a state campaign sends, record campaign-type-specific tracking +(`listmonk_campaign_type`) rather than the global `listmonk_sent_at`, so an +unrelated future campaign to the same carrier is not blocked. + +## Fulfillment expectation set at send time + +Every state-filing campaign points at a paid service whose fulfillment now +follows the `compliance_orders.fulfillment_status` machine (migration 086): + +``` +authorization_required -> authorization_signed -> awaiting_customer_delegation + -> awaiting_secure_credentials -> awaiting_government_fee_approval + -> awaiting_insurance_filing -> ready_to_file -> filed_waiting_state -> completed +``` + +The first customer touch after purchase is the **Limited Authorization to File** +e-sign (the signature is stamped exactly on the form's signature line), so the +campaign copy and order pages already disclose the authorization step and the +billed-at-cost government fees.