Convert OIG/SAM from one-time $299/yr to recurring $79/month (card+ACH only) -
the first real recurring-billing product in the system. Exclusion screening is
a *monthly* federal obligation, so recurring monitoring fits the requirement and
is the biggest valuation lever (vs a one-time annual run).
Catalog (single source of truth):
- service-catalog.ts: add billing_interval + allowed_methods to ComplianceService;
oig-sam-screening -> 7900c, billing_interval:"month", allowed_methods:[card,ach],
name "(Monthly Monitoring)".
- gen-service-catalog.py + check-service-catalog-drift.py: carry/guard the two new
fields; regenerate site catalog.
Checkout (api/src/routes/checkout.ts):
- mode:"subscription" with recurring price_data when billing_interval is set;
surcharge absorbed for recurring (clean $79/mo); server-side METHOD_NOT_ALLOWED
re-validation against allowed_methods.
- ensureColumns + migration 100: compliance_orders.stripe_subscription_id,
bundle_upsell_sent_at (+ subscription index).
Webhooks (api/src/routes/webhooks.ts):
- record stripe_subscription_id on checkout.session.completed (subscription mode).
- invoice.paid (subscription_cycle only) -> re-dispatch screening for the cycle;
invoice.payment_failed -> admin alert + first-failure customer nudge;
customer.subscription.deleted -> mark order cancelled. (API 2026-03-25 moved the
subscription link to invoice.parent.subscription_details.subscription.)
Fulfillment:
- job_server.py: pass recurring_cycle/invoice_id into the order.
- npi_provider.py: OIG handler labels renewal cycles "[Monthly cycle]" + re-screen
note; bundle action runs only the FIRST screening + flags the $79/mo upsell.
Bundle land-and-expand:
- Provider Compliance Bundle now includes only the first OIG/SAM screening (was
giving away $948/yr of monitoring inside an $899 bundle).
- new worker scripts/workers/bundle_upsell.py (+ pw-bundle-upsell timer): ~3 weeks
after a paid bundle, emails the customer to continue $79/mo monitoring; dedup via
bundle_upsell_sent_at; skips customers who already have an OIG/SAM order.
Surfaces updated to $79/mo: PaymentStep (filters methods, "Billed every month,
cancel anytime"), order pages, healthcare index, npi-compliance-check tool (also
fixed stale $699 bundle drift -> $899), hc_oig_screening + hc_compliance_bundle
emails.
Docs: billing.md gains a "Stripe-native Subscriptions" section + a reality-check
banner (Adyen/ERPNext-gateway model documented there is NOT live; Stripe is the
real rail). Fixed run-migrations.yml container name bug
(performancewest-postgres-1 -> performancewest-api-postgres-1, overridable).
Tests: api/tests/recurring-subscription.test.ts (28 assertions) covers catalog
gating, method validation, surcharge suppression, recurring line-item build,
invoiceSubscriptionId extraction, renewal-cycle gating. tsc clean; site build
clean; catalog drift OK.
Manual deploy step: enable invoice.paid, invoice.payment_failed,
customer.subscription.deleted on the Stripe webhook endpoint.
otc_reincorporation.html: redomesticate-to-Texas hook (Business Court + TXSE +
DE franchise-tax cost) personalized by state_inc_name/company/ticker, cross-sell
RA/foreign-qual/annual-report/franchise-tax, same-day coupon, lead-capture CTA to
/contact?service=reincorporation (high-touch corporate service, not self-serve),
careful 'not a law firm / not legal advice' disclaimers + CAN-SPAM address.
build_otc_campaign.py: emails only verified-email issuers from the harvest+scrape
+verify pipeline, --de-nv-only for the best reincorp fit, reuses trucking sender
plumbing + coupon. Per-deal value is high so capped modestly (400/run default).
UCR (Unified Carrier Registration) is annual: opens Oct 1, due Dec 31, mandatory
for interstate carriers (op A, same ~628k pool as IFTA) -> recurring revenue.
- build_ucr_annual_campaign.py: 3-touch business-day cadence (30/12/4 bd before
Dec 31, wider than IFTA since it's once a year), escalating tone, same-day
coupon, 'I already did it' suppression. Reuses build_trucking_campaigns +
IFTA business-day/token helpers (DRY). Per-year cycle reset.
- ucr_annual_reminder.html: deadline + fines/OOS risk + 'we figure out your fee
tier' + coupon + filed link + CAN-SPAM. Source campaign 473.
- migration 096: ucr_reminded_at / ucr_touch_no / ucr_self_filed_at.
- ifta.ts: add GET /api/v1/ucr/filed (shares the HMAC token scheme).
- checkout.ts: order-placement Telegram now shows 'Source: campaign (code X)'
when a discount code is present, so IFTA/UCR/CLIA conversions are visible.
(Confirmed order-alert Telegram already fires from handlePaymentComplete for
all compliance orders via both webhook + session paths.)
- Multi-touch reminders at 10/7/4 BUSINESS days before each deadline (weekends
skipped; biz-day math so a touch never lands purely on a weekend with no
runway). Escalating tone soft -> urgent -> last-chance, with the 'almost too
late to DIY, we can still file it' angle so it's a convenience sale, not a free
reminder service. ifta_touch_no tracks the highest touch sent so each touch
hits only carriers below that level; never repeats a touch.
- 'I already filed it' one-click link: HMAC-tokenized GET /api/v1/ifta/filed
(token matches between Python builder and api/src/routes/ifta.ts -- verified
identical output), records ifta_self_filed_at, friendly confirmation page,
stops further touches this cycle + gives DIY-vs-prospect signal. Builder
excludes self-filed carriers.
- migration 094 (ifta_touch_no) + 095 (ifta_self_filed_at); cycle reset clears
both each new quarter. Verified: biz-day touch schedule, token cross-match.
IFTA returns are due on fixed dates (Apr30/Jul31/Oct31/Jan31) and every
interstate carrier (op code A, ~628k sendable) files 4x/year forever -- pure
recurring revenue, no per-carrier deadline data needed.
- build_ifta_quarterly_campaign.py: self-gates to the reminder window (~21d
before each deadline), selects interstate carriers, mints the same-day coupon,
builds+schedules the campaign reusing build_trucking_campaigns plumbing (DRY:
one source of truth for sending/suppression/coupon). Per-quarter cycle reset
(ifta_reminder_cycle marker) so each quarter re-reminds the full pool; marks
ifta_reminded_at to avoid double-sends within a cycle.
- ifta_quarterly_reminder.html: deadline + penalties + 'we do the math' + coupon
+ CAN-SPAM. Listmonk source campaign id 469.
- migration 094: fmcsa_carriers.ifta_reminded_at column + partial index.
Verified: deadline/window logic correct, imports reuse tc helpers, migration
applied on prod.
Set up the CLIA recurring-renewal vein (every clinical lab renews its CLIA cert
on a 2-year cycle; CMS publishes the full lab file with expiration dates):
- service-catalog: clia-renewal ($449, discountable) + order page (npi-intake
steps) + intake manifest entry.
- harvest_clia_renewals.py: parse the CMS Provider-of-Services CLIA file, filter
to labs expiring within a window (default 120d), emit name/address/phone/expiry.
676k labs -> ~70k expiring in the next ~4 months.
- match_clia_to_nppes.py: CLIA has no NPI/email, so bridge to emailable NPPES
orgs by normalized name+zip to recover NPI+email (yield TBD; labs that do not
match still have clean phone+postal for a phone/mail channel).
- hc_clia_renewal.html: warm turnover-safety-net email with the striped official-
record card (CLIA #, expiry, status), verify-on-CMS-QCOR, founder guarantee
card, full CAN-SPAM address.
- Upgrade the plain teal record banner to the authoritative barber-pole 'Official
record' banner in the personal/turnover/overdue-personal templates (the switch
to personal templates had dropped the striped look from live revalidation sends).
- nppes_outdated: replace plain info table with the striped 'Official record -
NPPES NPI Registry' card (status honestly labeled as our compliance flag).
- Wire revalidation_overdue -> hc_revalidation_overdue_personal.html with a direct
past-due subject ('Your Medicare revalidation is past due - let's get it filed')
and PAST DUE status + days-overdue in the record card; due_soon stays warm.
- Striped card now on all 7 templates that show a real record; oig_screening and
compliance_bundle correctly omit it (no specific record to display).
Match the authoritative 'official record' look of the revalidation emails on the
deactivated/NPI-reactivation template: striped banner + structured NPPES record
card. Kept it accurate -- NPI/name are labeled NPPES (the real public source);
the deactivation status is labeled as our compliance flag (not an NPPES field),
since deactivation is not a single public dataset, with a 'confirm via official
sources' footnote.
Per your call: add the same personal founder card (headshot linked to /about,
service-neutral satisfaction-guarantee quote, signature, title) to the four
remaining HC templates for a consistent trust signal across all healthcare
outreach. Kept the factually-direct subjects where the situation IS past-due/
deactivated (npi_reactivation) -- only the framing softens, not the facts.
All HC templates now use the v2 signature.
CAN-SPAM requires a valid physical postal address in every commercial email.
All 8 HC campaign templates and the FCC campaign_template.html only had
'Cheyenne, WY' (no street) -- added the full
'525 Randall Ave Ste 100-1195, Cheyenne, WY 82001' to match the (already-correct)
trucking templates. Audited every Listmonk source/sent campaign + wrapper
templates: all active sends carry address + unsubscribe.
Also: revalidation segments now use hc_revalidation_personal.html with subject
'Let's make sure your Medicare revalidation is handled in time'.
Adds hc_revalidation_personal.html: the turnover safety-net email plus a
personal guarantee card from Justin Hannah -- round headshot (links to /about so
readers can confirm a real person stands behind it), an italic satisfaction-
guarantee quote ('I will personally make it right... that is my promise'), a
rendered 'Justin Hannah' signature (Dancing Script, SIL OFL), and his title
(Founder & Principal Consultant). Signature image generated via PIL and added to
site/public/images/justin-signature.png. Test sent to justin@.
New HC template (hc_revalidation_turnover.html) that gets the warm, 'someone who
has our back' feel WITHOUT falsely claiming a prior business relationship (which
would be a deceptive practice under FTC/UDAP and is especially risky with
compliance-minded healthcare admins). Instead it leans on:
- the real staff-turnover insight ('whoever last handled this may have moved on')
- genuine relevance (their actual NPI + CMS revalidation due date)
- the safety-net positioning ('we keep an eye on this so it does not become your
problem' / 'we will make sure it gets done right no matter who handled it')
- true social proof (trusted by providers nationwide) + verify-on-CMS.gov
Every claim is true and defensible. Test sent to justin@.
The HC warmup pool is supply-constrained (~400 verified providers, all fed by
the same narrow 'revalidation 1-90 days OVERDUE' slice). This adds a mirror-image
proactive segment that targets providers whose Medicare revalidation is UPCOMING
within the next 1-90 days, drawn from the same CMS Revalidation Due Date List --
no new data source needed. 'Handle it before your deadline' is a strong pitch and
roughly doubles the deliverable pool.
- New selector reval_due_soon (status=upcoming, days_until in [HC_DUE_SOON_MIN,
HC_DUE_SOON_MAX] default 1-90).
- New segment revalidation_due_soon reusing the existing /order/npi-revalidation
service ($599) with template hc_revalidation_due_soon.html.
- attribs_for now exposes days_until (positive days to due date).
- Added to ACTIVE_SEGMENTS.
EDGAR issuer names are messy (ALL-CAPS, trailing /DE/ /NV state tags, entity
suffixes) and render badly in a personalized subject line. Added:
display_name -- title-cased, state-tag-stripped, suffix kept
short_name -- suffix dropped too, for a punchy subject merge field
e.g. 'WINDTREE THERAPEUTICS INC /DE/' -> 'Windtree Therapeutics'
So 'Is DEXIT in the cards for {{short_name}}?' renders clean. ~98% of short_names
are <=40 chars (survive mobile subject truncation).
The master file lists warrants/units as separate tickers under one CIK, so the
pull now dedupes to one row per company (other tickers kept in all_tickers).
data/otc_leads.csv: 861 unique active US-domestic microcap OTC issuers
(<$75M float, all actively filing, 100% with business address + phone). By
incorporation: DE 365, NV 325 (DE+NV=690 = the reincorporation targets), WY 44,
FL 39, MD 38. Dropped from the 2,771 OTC universe: 1,672 foreign, 62
accelerated/large filers, 73 delinquent/dark. EDGAR has no email -> phone +
address captured for enrichment / direct mail / call.
Refresh (hc_data_refresh.py):
- CRITICAL: drop optout_ending from REFRESHED_FIELDS -- the refresh never
computes it, so propagating it blanked the channel CSVs and would starve the
compliance_bundle segment (whose selector IS optout_ending).
- MAJOR: only rewrite leie_excluded when OIG was actually pulled (guard was
'not skip_oig OR not skip_sam', so a --skip-oig run blanked all exclusion
flags). Also write 'Y' (matching the original list builder) not '1'.
- Use 'no_reval_flag' (the original vocabulary) instead of 'not_on_list' when an
NPI drops off the reval list, and clear reval_due_date too.
- Throttle politeness: move time.sleep(0.05) above the early-continue paths so
EVERY CMS request is spaced, not just the minority that are on the list.
- Guard blank-NPI rows (leave their status untouched instead of mislabeling).
- Master write preserves any columns beyond HEADER (no silent column drop).
Warmup cron (build_healthcare_campaigns_cron.py):
- Fix the daily-slice split: it summed to less than the budget (dropped ~2/day)
and could OVERSHOOT on tiny totals (each 'other' floored to >=1). Now uses
divmod for an even remainder and reclaims rounding onto the lead, so
sum(per_seg) == total_slice exactly for every input (verified 0,1,2,7,100,300).
Templates: the non-revalidation emails rendered {{ .Subscriber.Attribs.detail }}
(a reval due date) under a 'Practice'/'Status'/'Record' label -- a wrong/
confusing personalization on a live send (esp. OIG, selector 'any'). All four
now show the practice name; 'detail' is retired from rendering (revalidation
uses reval_due_date/days_overdue directly).
The revalidation email had a 'check the official CMS record yourself' proof
block (the strongest trust signal), but the other four healthcare programs had
none -- just the generic SOC2/guarantee footer. Each now points the provider to
the actual public government source that backs its claim:
- NPPES outdated -> 'Look up my NPI on NPPES' (npiregistry.cms.hhs.gov, fully
public; shows the exact address/taxonomy/contact payers and CMS see).
- OIG screening -> 'Search OIG LEIE / Search SAM.gov' (exclusions.oig.hhs.gov +
sam.gov), with an honest note that a one-time self-search isn't the documented
recurring screening CMS expects.
- Reactivation (deactivated) -> deactivation isn't a single public dataset, so
this is framed honestly: most deactivations follow a lapsed revalidation
(public CMS Revalidation list) and show in NPPES; also 'are your claims
paying?' as a self-check. No fabricated 'deactivated record' card.
- Compliance bundle -> all four official sources (CMS Revalidation, NPPES, OIG
LEIE, SAM.gov) it monitors year-round.
All four government URLs verified reachable (200/302). No paper/mail filing
mechanics revealed; CMS/NPPES/OIG/SAM public names are fine and signal
expertise.
- Keep public CMS terms (CMS-855, PECOS) in client copy; the rule is about not
exposing the paper/mail filing mechanic, not public form/system names.
- Wrap the bare 'service fee / $599' row in a bordered card with a prominent
green price so it no longer floats awkwardly under the verify box.
Swap the dark slate stripe for a bright blue/white/red diagonal barber-pole
pattern; keep the header text readable via a translucent dark band behind it.
Adds a teal 'No logins, no 2FA codes, no headaches' card (between the fee and
the CTA): we do the whole revalidation, you never share a password or chase a
two-factor code, just a one-minute e-signature. Mirrors the npi-revalidation
service page's convenience pitch, kept clean of form numbers.
Adds a subtle barber-shop diagonal stripe (repeating-linear-gradient over the
solid slate bg) to the CMS official-record card header. Layered over the solid
#1e293b so clients that ignore the gradient (Outlook) still get the dark bar.
- Add the 'Performance West is an independent compliance firm, not affiliated
with CMS or Medicare' footer disclaimer to the 4 remaining HC emails
(reactivation, NPPES, OIG/SAM, bundle), matching the revalidation email.
OIG email also names the OIG and SAM.gov it references.
- Scrub client-facing mechanics: drop the CMS-855 form number from the
reactivation CTA and the PECOS system name from the revalidation CTA; clean
the same out of source comments.
Keeps the official-record card clean (just the data.cms.gov source line) and
puts the not-affiliated disclaimer in the standard footer alongside the
company line.
Skepticism ("is this even real?") is the top objection. The data IS accurate
(verified our subscribers' NPIs match the official CMS Revalidation Due Date List
exactly), so this is a credibility-presentation fix:
1. Email: replace the plain detail row with an "Official record - CMS Medicare
Revalidation Due Date List" card (NPI, legal name, due date, days overdue)
plus a "Verify on CMS.gov" button. Clearly labeled as our presentation of
public CMS data, not a CMS screenshot (no impersonation).
2. API: npi/lookup now pulls the revalidation due date LIVE from the public CMS
dataset (data.cms.gov) instead of the empty local table, and returns a
revalidation{ due_date, source, cms_legal_name, verify_url } proof object.
3. Tool: /tools/npi-compliance-check shows a live "official record" card with a
self-verify link when CMS returns a due date.
Builder now stores reval_due_date/days_overdue as separate attribs for the card
(existing 194 subscribers backfilled from their detail string).