#!/usr/bin/env python3
"""
create_crtc_usf_campaign.py — One-off CRTC marketing email hooked on the
Q3 2026 USF contribution-factor increase.
Audience : Listmonk list 3 (FCC Carriers - Direct Contacts, ~6,046 enabled).
Hook : Q3 2026 federal USF contribution factor = 38.8% (up from 37.0% in
Q2), effective Jul 1 (FCC PN DA-26-546A1).
Offer : $200 off the CRTC carrier package service fee with code CANADA200,
valid through Fri Jun 19 2026 23:59 ET. The CRTC order page
auto-applies ?code= from the URL, so the CTA links carry it.
Lead : "Canadian Wholesale Carrier & Vendor Reference Guide" PDF, hosted at
magnet performancewest.net/guides/canada-carrier-guide.pdf (Listmonk
campaigns can't attach files, so we link a prominent download).
Creates the campaign in Listmonk as a DRAFT. Sending is a separate, manual,
STOP-and-confirm step in the Listmonk UI (or set status via API). Run:
python3 scripts/workers/create_crtc_usf_campaign.py # draft on list 3
python3 scripts/workers/create_crtc_usf_campaign.py --test # draft to a test list/email
"""
import argparse
import os
import sys
# Make `scripts` importable whether run from repo root or scripts/workers.
_REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if _REPO not in sys.path:
sys.path.insert(0, _REPO)
from scripts._email_plaintext import html_to_text # noqa: E402
from scripts.workers.campaign_helpers import ( # noqa: E402
CONTACT, P, PS, H2, UL, bq, cta, hdr, flagbar, stats, assemble, ftr,
create_campaign,
)
LIST_ID = 3
CODE = "CANADA200"
GUIDE_URL = "https://performancewest.net/guides/canada-carrier-guide.pdf"
ORDER_URL = f"https://performancewest.net/order/canada-crtc?code={CODE}"
def coupon_banner():
"""Prominent $200-off banner with expiry."""
return (
'
'
'| '
' Limited-time offer '
f'$200 off your Canadian carrier setup '
f'Use code {CODE} at checkout · expires Friday at 11:59pm ET '
' |
'
)
def guide_block():
"""PDF lead-magnet download block (Listmonk can't attach files)."""
return (
''
''
''
'| 📄 | '
''
' Free guide: Canadian Wholesale Carrier & Vendor Reference '
'12 vetted Canadian wholesale partners for voice termination, SIP trunking, UCaaS, and both US and Canadian DIDs — the upstream vendors you’ll work with once your Canadian carrier is live, and which of them can provision US numbers. '
f'Download the PDF →'
' | '
' |
'
)
def build_body():
return (
P("Hi {{ .Subscriber.FirstName }},")
+ P("If you contribute to the federal Universal Service Fund, your Q3 number just went up again.")
+ bq("The FCC has set the Q3 2026 USF contribution factor at 38.8% — up from 37.0% in Q2, and effective July 1. That is the rate US carriers remit on the interstate and international end-user revenue they report on the 499.")
+ stats(
("38.8%", "Q3 2026 USF
contribution factor"),
("+1.8 pts", "increase over
Q2 (37.0%)"),
("Jul 1", "effective date
(FCC DA-26-546A1)"),
)
+ P("38.8% is near the highest the factor has ever been — a decade ago it sat in the mid-teens. And USF is just one line on a long list. Here is the full load a US-registered voice provider carries today:")
+ UL(
"USF contributions — 38.8% of interstate/international end-user revenue, filed and remitted to USAC via the 499",
"FCC Form 499-A / 499-Q — annual and quarterly revenue filings, with true-ups and audit exposure",
"Robocall Mitigation Database — annual recertification; miss it and your traffic gets blocked",
"STIR/SHAKEN — standing up and running your own call-authentication / signing posture",
"Photo-ID “Know Your Customer” rules — under the FCC’s 2025 Robocall Mitigation Order, collecting and authenticating a government-issued photo ID for every new customer before you turn up service",
"CALEA — lawful-intercept capability and SSI filing",
"Section 214 + Team Telecom, state PUC registrations, FCC regulatory fees — on top of all of the above",
)
+ H2("The idea: run your whole VoIP business as a Canadian carrier.")
+ P("Not a side entity or a backup — your actual home base. You set up one CRTC-registered Canadian carrier, put your customers on it from anywhere (US, Canada, or international), and your nexus — the carrier of record, your billing, banking, contracts, and regulatory home — sits in Canada.")
+ bq("As a Canadian reseller you don’t report to the FCC, you don’t tax your customers for USAC, and there is no STIR/SHAKEN program for you to build and operate. Those obligations live with the upstream wholesale carriers you buy from — not with you.")
+ H2("What that means in practice.")
+ UL(
"No FCC reporting. A Canadian reseller that isn’t an FCC-registered carrier has nothing to file with the FCC — no 499-A, no 499-Q, no RMD recertification",
"No USAC/USF on your revenue. You don’t register with USAC or remit the 38.8% contribution — that obligation sits upstream, not on your customer billing",
"No STIR/SHAKEN to set up or run. A reseller can’t even be issued a US signing token — the upstream carrier that owns the numbers signs the calls. There is no authentication program for you to stand up, certify, or maintain",
"No FCC photo-ID mandate and no CALEA build-out in the US sense for your Canadian entity",
"No FCC Section 214, no ongoing 214 burden. Where the FCC requires an international Section 214 authorization (with Team Telecom review and continuing obligations), the CRTC equivalent — a BITS registration — is a simple, low-cost notification with no ongoing 214-style burden",
"Customers from anywhere. Onboard US, Canadian, or international customers onto one Canadian carrier — same +1 dialing, nothing changes on their end",
"US numbers still work. Several Canadian wholesale carriers provision US DIDs to CRTC-registered carriers, so you can serve US customers directly — the free guide below lists which ones",
"One clean jurisdiction. Your carrier of record, banking, and contracts all sit in Canada — outside the FCC’s reach",
)
+ H2("“How do I terminate to the US then?”")
+ P("Routinely. Many US-based long-distance termination operators and wholesale carriers actively accept traffic from Canadian carriers — cross-border voice is one of the most common interconnects there is. You buy US numbers and US termination from a wholesale partner, and they handle the US-side STIR/SHAKEN signing on the way out — exactly how the vast majority of small carriers already rely on an upstream provider to sign for them. Your Canadian-origin traffic falls under the CRTC’s lighter regime, handled by your Canadian trunking provider.")
+ P("The point: the heavy US compliance stack — USF, the 499s, the RMD, photo-ID KYC, the signing certificates — rides on the upstream wholesale carriers, not on your day-to-day operation.")
+ H2("What we set up — turnkey, in 6–10 weeks.")
+ UL(
"Incorporation in British Columbia or Ontario — your Canadian carrier entity",
"CRTC registration (domestic reseller + BITS international authorization)",
"Canadian DID provisioned under your new carrier identity",
"Virtual registered office, .ca domain + up to 14 email addresses",
"Full corporate binder, delivered digitally — plus a Canadian business-banking referral",
)
+ coupon_banner()
+ cta("Start your Canadian carrier setup — $200 off →", ORDER_URL)
+ guide_block()
+ PS(f"Questions about moving your VoIP onto a Canadian carrier? {CONTACT}. The {CODE} discount is good through Friday at 11:59pm ET.")
+ P("— Performance West")
)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--test", action="store_true", help="create against a test list id (env CRTC_TEST_LIST) instead of list 3")
ap.add_argument("--name", default="CRTC USF Q3 \u2014 38.8% increase + $200 off (CANADA200)")
ap.add_argument("--subject", default="USF jumps to 38.8% \u2014 run your VoIP as a Canadian carrier instead ($200 off)")
args = ap.parse_args()
lists = [int(os.getenv("CRTC_TEST_LIST", "0"))] if args.test else [LIST_ID]
if args.test and lists == [0]:
print("--test requires CRTC_TEST_LIST env var (a test list id)", file=sys.stderr)
return 1
body = assemble(
hdr(
"USF Increase \u2014 Q3 2026",
"USF just hit 38.8%.
Run your VoIP as a Canadian carrier.",
"One CRTC carrier, nexus in Canada, customers from anywhere",
),
flagbar(
"US carrier \u2014 38.8% USF + the full FCC stack",
"Canadian carrier \u2014 no FCC reporting, no USAC, no S/S to run",
),
build_body(),
ftr(""),
)
altbody = html_to_text(body)
print(f"=== Creating CRTC USF campaign (list {lists}) ===")
cid = create_campaign(args.name, args.subject, lists, body, altbody=altbody, status="draft")
if cid:
print(f"\nDraft created (id {cid}). Review/preview in Listmonk, then send manually.")
print(f"Body: {len(body):,} chars HTML / {len(altbody):,} chars plaintext")
return 0 if cid else 1
if __name__ == "__main__":
raise SystemExit(main())