Implemented notifications catalog#
Long-form reference for every outbound notification the AutoResponder pipeline sends today: exact To / Cc, subject templates, attachments, and body text as implemented in code.
For the compact recipient matrix, see NOTIFICATIONS_REFERENCE.html. Product decisions from the 2026-05-17 alignment are in meetingNotes.md. Deferred work: Deferred (N11) and IMPLEMENTATION_PLAN.html. Legacy 1.x / 2.x / 3.x IDs are mapped in Legacy ID mapping.
Derived from:
src/auto_responder/services/notifications/notifier.pysrc/auto_responder/services/notifications/alignment_helpers.pysrc/auto_responder/processors/action_engine.py(flush_batch_notifications)src/auto_responder/main.pysrc/auto_responder/services/tarun_upload_api.pysrc/auto_responder/services/email_service.pysrc/auto_responder/config.pysrc/auto_responder/send_output_folder_emails.pysrc/auto_responder/send_action_items_confirmation.py
Notification index (N01–N12)#
Rows are sorted in pipeline send order. Every live email also receives the global Max + Vish Cc (see Global delivery rules) unless SMTP_NOTIFICATION_RECIPIENTS override is active. Index Cc shows role-specific Cc only; add Max + Vish for production routing unless override is on.
| ID | Phase | Name | Method | To | Cc | Attachments | Subject |
|---|---|---|---|---|---|---|---|
| N01 | Batch | Hodor Prospect Import | notify_hodor_prospect_import | Venu | Max, Vish | hodor_prospect_import_verification.csv | Hodor Prospect Import ready… |
| N02 | Batch | Multipub follow-up | notify_multipub_subscriber_followup_batch | Angel+Yogesh | Matt, Max, Vish | multipub_subscriber_followup.csv | Subscriber follow-up needed… |
| N03 | Batch | Tarun undetermined sender | notify_tarun_undetermined_sender_review | Tarun | Max, Vish | tarun_undetermined_senders.csv | Undetermined sender review… |
| N04 | Run end | Marketing suppression | notify_marketing_suppression | Marketing team | Max, Vish | {BusinessUnit}_NoLongerThere_{YYYY-MM-DD}.csv (e.g. Marketing_NoLongerThere_2026-05-27.csv) | Marketing suppression list… |
| N05 | Run end | Sai replacements | notify_sai_action_items | Sai | Max, Vish | output_document_alternate_contacts.csv | New replacements… |
| N06 | Run end | Sai manual review | notify_sai_action_items | Sai | Max, Vish | Up to 4 CSVs | Manual review action items… |
| N07 | Run end | Venu CUPOLA audit | notify_venu_cupola_audit_files | Venu | Sai, Max, Vish | Audit + HR CSV | CUPOLA audit log ready… |
| N08 | Run end | IP4 run audit | notify_run_audit_for_ip4 | Sai | Max, Vish | Log, CSVs, HTML | Run reports (no ACTION REQUIRED) |
| N09 | Upload | Tarun upload Yes | notify_multipub_subscriber_followup_from_upload | Angel+Yogesh | Matt, Tarun, Max, Vish | Allowlisted uploaded file (+ multipub_subscriber_followup.csv when items passed) | Tarun confirmed… |
| N10 | Upload | Tarun upload No | append_tarun_upload_audit_row | No email | |||
| N11 | Deferred | Leadership summary | notify_run_summary_for_leadership | Log only | |||
| N12 | CLI (post-run) | Action items completion confirmation | send_action_items_completion_confirmation | Same as N01–N07 owner per group | Same as original notification | None | Confirm completion — {original N## title}… |
Pipeline order: N01 → N02 → N03 (batch flush; each sends only when its in-memory queue has rows) → N04 → N05 or N06 when action-item CSVs exist → N07 → N08 → N11 scaffold (log only). N09/N10 are independent of the batch run.
Global delivery rules#
- Single send path: every live
notify_*method callsNotifier._send_notification_email, which assembles the full body (see Email body assembly), merges Cc, and invokesEmailService.send_email. - Global Cc:
NOTIFICATION_EMAIL_MAXandNOTIFICATION_EMAIL_VISHare appended on every send via_global_cc()/_merge_cc(deduped against To). These appear in the delivery note Intended Cc when override is active. - Action-required subjects:
_action_required_subject()produces[ACTION REQUIRED] [AutoResponder] …and strips duplicate prior[REVIEW REQUIRED]/ bare[AutoResponder]prefixes. Exception:notify_run_audit_for_ip4(N08) uses only[AutoResponder] Run reports - {run_dir}. - Attachment allowlist:
filter_notification_attachmentskeeps.csv,.txt,.log,.htmlonly — JSON and other suffixes are dropped from emails (JSON may still exist on disk). This also applies to Tarun uploads: the upload endpoint accepts.csv,.tsv,.xls, and.xlsx, but only allowlisted suffixes are attached to the outgoing notification. SMTP_NOTIFICATION_RECIPIENTS: when set,EmailService.send_emaildelivers To-only to that list — all Cc is dropped at SMTP time. The body still records who would have received the message in production via the Delivery note block (see below).SMTP_ALWAYS_CC: merged into Cc byEmailService.send_emailwhen override is off (after notifier global Cc). When override is on, the delivery note listsSMTP_ALWAYS_CCwith an explicit “not added to delivery” qualifier.- Simulation / dev mock-all:
MOCK_ALL_SERVICES_FOR_DEV=truealways usesNotifier(dry_run=True)and sends nothing. WithWRITE_OPERATIONS_ENABLED=false, notifications are logged only unlessSMTP_NOTIFICATION_RECIPIENTSis set; in that simulation+override mode, database writes remain intercepted but notifications are sent To-only to the override list. - Skip-when-unset: most methods return
Trueand log when the primary recipient env var is missing (no hard failure). Client Services N02/N09 returnFalseonly when Angel/Yogesh are unset and the Max fallback is also unset. - Multipub Client Services gate: subscriber follow-up emails fire only when Multipub shows active subscription, subscription in the last 12 months, or purchase in the last 12 months. Undetermined sender identity routes to Tarun; determined sender with no Multipub match sends neither Tarun nor Client Services email.
Email body assembly (every live send)#
Notifier._send_notification_email builds the SMTP body in this order (implemented in notifier.py + alignment_helpers.py). Every send includes an HTML alternative that bolds the subject and opening instructions. When SMTP_NOTIFICATION_RAW_MODE is false, metadata blocks are appended at the bottom of both plain and HTML parts.
- Main content — per
notify_*method (documented under each N## below). - Catalog banner — appended via
format_notification_catalog_headeronly whenSMTP_NOTIFICATION_RECIPIENTSis set, so dev/override recipients can map the message to a catalog entry. Production recipients (Sai, Venu, etc.) do not see the banner. - Attachments guide — when at least one attachment survives the allowlist,
format_attachment_guideappends a filename + one-line purpose for each file. - Delivery note — when
SMTP_NOTIFICATION_RECIPIENTSis non-empty,_smtp_override_disclosure_linesappends who actually received the message vs who it was intended for in production.
1. Catalog banner (SMTP override only)#
Omitted in production when mail goes to the intended role addresses. When SMTP_NOTIFICATION_RECIPIENTS is set, the banner is the last metadata block before the delivery note (after main content and attachments guide).
This message is notification catalog {ID} — {title from NOTIFICATION_CATALOG_NAMES}.
{optional one-line usage hint — N08 only today}
(See docs/NOTIFICATIONS_CATALOG.html)
{main content begins here}| ID | Banner title (NOTIFICATION_CATALOG_NAMES) |
|---|---|
| N01 | Hodor Prospect Import |
| N02 | Multipub subscriber follow-up |
| N03 | Tarun undetermined sender |
| N04 | Marketing suppression |
| N05 | New replacements for inactive contacts (Sai / IP4) |
| N06 | Manual review action items (Sai / IP4) |
| N07 | Venu CUPOLA audit |
| N08 | Run audit (IP4) |
| N09 | Tarun upload — Yes (subscriber follow-up) |
| N10 | (no email — log only) |
| N11 | Leadership run summary (deferred; not emailed) |
| N12 | Action items completion confirmation |
2. Main content (per notification)#
Everything after the banner until the attachments guide. Documented in full under each N01–N09 entry below, including per-notification Intended To / Intended Cc values used in the delivery note when SMTP_NOTIFICATION_RECIPIENTS is set.
3. Attachments guide (when files attach)#
Attachments:
- {filename}: {one-line purpose from NOTIFICATION_ATTACHMENT_DESCRIPTIONS}Unknown filenames fall back to: Supporting file from this run (see filename). output_document_*.csv files use: Output-document deliverable for this run.
4. Delivery note (when SMTP_NOTIFICATION_RECIPIENTS is set)#
Appended after the main content and attachments guide. Lists the override mailbox(es) that actually received the message and the intended production routing for that notification (To/Cc as computed before override). Each N## entry below documents its intended To/Cc for this footer.
--- Delivery note ---
SMTP_NOTIFICATION_RECIPIENTS is active; this message was delivered to the override list.
Override To : {comma-separated addresses from SMTP_NOTIFICATION_RECIPIENTS}
Intended To : {production To for this notification, or "(none)"}
Intended Cc : {production Cc after _merge_cc — per-notification Cc + global Max + Vish, deduped vs To; or "(none)"}
SMTP_ALWAYS_CC : {comma-separated from SMTP_ALWAYS_CC} (not added to delivery while override is active) ← only when SMTP_ALWAYS_CC is setImportant: when override is active, SMTP sends To-only to Override To. Intended Cc (including Max, Vish, Matt, Sai, Tarun, etc.) and SMTP_ALWAYS_CC are informational in the body only — neither is added at SMTP time while override is on.
Configuration#
| Env var | Role | Used by |
|---|---|---|
NOTIFICATION_EMAIL_ANGEL | Client Services | N02 + N09 |
NOTIFICATION_EMAIL_YOGESH | Client Services | Same as Angel |
NOTIFICATION_EMAIL_MATT | Client Services Cc | Multipub follow-up + Tarun upload Yes |
NOTIFICATION_EMAIL_MAX | Supervisor | Global Cc; Venu audit per-row Cc |
NOTIFICATION_EMAIL_VISH | Supervisor | Global Cc |
NOTIFICATION_EMAIL_TARUN | Multipub review | N03; Cc on N09 |
NOTIFICATION_EMAIL_SAI | IP4 / manual Cupola | N05/N06 + N08 (ip4_audit_recipients()) |
NOTIFICATION_EMAIL_VENU | Venu | N01 Hodor Prospect Import To; N07 CUPOLA audit To |
NOTIFICATION_EMAIL_ERIN | Marketing team (N04) | Shared marketing mailbox — marketing-team-cbi@columbiabooks.com (env name is legacy; not a personal Erin inbox) |
TARUN_UPLOAD_DIR | Upload persistence | POST /multipub/upload (default ./tarun_uploads) |
SMTP_NOTIFICATION_RECIPIENTS | Override To | All notifications |
SMTP_ALWAYS_CC | Extra Cc | All notifications when override off |
NOTIFICATION_EMAIL_LEADERSHIP | Leadership summary | Deferred — notify_run_summary_for_leadership (log-only today) |
MOCK_ALL_SERVICES_FOR_DEV | Dev / CI mock-all | Mock connectors; notifier dry-run |
WRITE_OPERATIONS_ENABLED | Simulation (no writes) | When false, database writes are intercepted; notifications are log-only unless SMTP_NOTIFICATION_RECIPIENTS is set, in which case they send To-only to the override list. |
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM | SMTP transport | All sends via EmailService |
TARUN_UPLOAD_API_* | Upload API | Optional FastAPI host for POST /multipub/upload (see .env.example) |
Notification details (by phase)#
Each entry documents: Method, Trigger, To, Cc, Subject, Attachments, and the complete body as assembled by _send_notification_email (banner → main content → attachments guide → delivery note). See Email body assembly for shared blocks.
Batch notifications — N01–N03 (flush_batch_notifications)#
Flush order in code: N01 → N02 → N03. In the normal batch run, each method is called only when its corresponding queue has rows. The CSV-only fallback behavior documented for N02/N03 is used by the resend CLI and any direct method call with an existing attachment directory.
N01 — notify_hodor_prospect_import#
| Field | Value |
|---|---|
| Catalog ID | N01 |
| Trigger | Queued Prospect Import rows during ActionEngine.execute() when Hodor automatic updates are off. Flushed at end of run; skipped when queue empty. |
| To | NOTIFICATION_EMAIL_VENU (skips cleanly when unset) |
| Cc | Global Max + Vish only |
| Subject | [ACTION REQUIRED] [AutoResponder] Hodor Prospect Import ready for manual ingestion - {n} row(s) |
| Attachments | hodor_prospect_import_verification.csv only |
Body (fixed in notifier.py):
Attached: Hodor Prospect Import verification rows generated by AutoResponder.
hodor_prospect_import_verification.csv is the review and update artifact for this run. Each row includes InboxSource (original mailbox), Email, Action, RequestedStatus, Title, ProsNum, Reason, PreviousEmail, and all SAC contact fields (comma-delimited UTF-8).
Use this file to review and apply Hodor updates manually.Verification metadata columns (prepended in hodor_prospect_import_verification.csv): InboxSource, Email, Action, RequestedStatus, Title, ProsNum, Reason, PreviousEmail — then all SAC columns (sal, fname, …, employee_value).
Complete body (as sent) — intended routing for delivery note:
- Intended To:
NOTIFICATION_EMAIL_VENU - Intended Cc:
NOTIFICATION_EMAIL_MAX,NOTIFICATION_EMAIL_VISH(global; deduped if same as To)
This message is notification catalog N01 — Hodor Prospect Import.
(See docs/NOTIFICATIONS_CATALOG.html)
Attached: Hodor Prospect Import verification rows generated by AutoResponder.
hodor_prospect_import_verification.csv is the review and update artifact for this run (InboxSource, Action, RequestedStatus, SAC fields).
Attachments:
- hodor_prospect_import_verification.csv: Hodor Prospect Import verification rows for manual review and updates.
--- Delivery note --- ← only when SMTP_NOTIFICATION_RECIPIENTS is set
SMTP_NOTIFICATION_RECIPIENTS is active; this message was delivered to the override list.
Override To : {SMTP_NOTIFICATION_RECIPIENTS}
Intended To : {NOTIFICATION_EMAIL_VENU}
Intended Cc : {NOTIFICATION_EMAIL_MAX}, {NOTIFICATION_EMAIL_VISH} (or "(none)" if unset)N02 — notify_multipub_subscriber_followup_batch#
| Field | Value |
|---|---|
| Catalog ID | N02 |
| Trigger | Queued during ActionEngine.execute() for determined-sender Multipub evidence (active sub, sub in last 12 months, or purchase in last 12 months), including inactive/left-org and matched undeliverable paths. Flushed once at end of run only when the queue has rows. CSV-only fallback sends only when the notifier method is called directly with an empty queue and multipub_subscriber_followup.csv already exists, as in the resend CLI. |
| To | NOTIFICATION_EMAIL_ANGEL + NOTIFICATION_EMAIL_YOGESH (falls back to Max if both unset) |
| Cc | Matt when set; plus global Max + Vish |
| Subject | [ACTION REQUIRED] [AutoResponder] Subscriber follow-up needed - {count} contact(s) |
| Attachments | multipub_subscriber_followup.csv — columns: contact_name, contact_email, subsnum, org, determination, active_subscription_count, last_12mo_subscription_count, last_12mo_purchase_count, product_list, source_subject, source_message_id, match_method, review_reason |
Body (from build_multipub_subscriber_followup_payload in alignment_helpers.py):
The auto-responder detected that the people below are no longer available, retired, or unreachable, but they likely still have active or recent Multipub subscriptions.
Please reach out to these customers (or related contacts) and make the necessary subscription and contact adjustments.
Total contacts: {N}
============================================================
{idx}. {contact_name} ({contact_email})
Subscriber number: {subsnum}
Org: {org} (when present)
Determination: {determination} (when present)
Active subscriptions ({total}):
- onum {onum}, {product_name}, expires {end_issue_date}
... and {N} more (when more than 5)
Subscriptions ended in last 12 months ({total}): ...
Single-issue purchases in last 12 months ({total}): ...
Source subject : {source_subject}
Source message id: {source_message_id}CSV-only fallback (empty in-memory queue but multipub_subscriber_followup.csv on disk): subject [ACTION REQUIRED] [AutoResponder] Subscriber follow-up needed (see attached CSV); main content:
Please review the attached CSV. The auto-responder detected contacts who are no longer available but likely still have active or recent Multipub subscriptions. Reach out to customers and make the necessary adjustments.Complete body (as sent) — intended routing for delivery note:
- Intended To:
NOTIFICATION_EMAIL_ANGEL+NOTIFICATION_EMAIL_YOGESH(if both unset →NOTIFICATION_EMAIL_MAXonly) - Intended Cc:
NOTIFICATION_EMAIL_MATTwhen set and not already in To; plus global Max + Vish
This message is notification catalog N02 — Multipub subscriber follow-up.
(See docs/NOTIFICATIONS_CATALOG.html)
{main content — full per-contact blocks above, or CSV-only fallback paragraph}
Attachments:
- multipub_subscriber_followup.csv: Client Services queue: inactive/unavailable contacts with Multipub activity.
--- Delivery note ---
SMTP_NOTIFICATION_RECIPIENTS is active; this message was delivered to the override list.
Override To : {SMTP_NOTIFICATION_RECIPIENTS}
Intended To : {Angel}, {Yogesh} (or Max if Angel/Yogesh unset)
Intended Cc : {Matt when set}, {Max}, {Vish} (Matt omitted if already in To)N03 — notify_tarun_undetermined_sender_review#
| Field | Value |
|---|---|
| Catalog ID | N03 |
| Trigger | Queued when sender_identity_determined is false (e.g. source email is Manual Review Required). Flushed at end of run only when the queue has rows. CSV-only fallback sends only when the notifier method is called directly with an empty queue and tarun_undetermined_senders.csv exists on disk, as in the resend CLI. |
| To | NOTIFICATION_EMAIL_TARUN (skips cleanly when unset) |
| Cc | Global Max + Vish only |
| Subject | [ACTION REQUIRED] [AutoResponder] Undetermined sender review - {count} case(s) |
| Attachments | tarun_undetermined_senders.csv — columns: message_id, raw_from, parsed_candidates, subject, body_excerpt, determination, run_directory |
Body (from build_tarun_undetermined_sender_payload):
The auto-responder could not determine the true sender email for the messages below.
Please review the email bodies, match any subscriber you can find in Multipub, and respond through the Multipub upload endpoint:
Yes - you found a subscriber (this will notify Client Services).
No - reviewed; no further action needed (logged only).
Total cases: {N}
============================================================
{idx}. Subject : {subject}
Raw From : {raw_from}
Candidate(s) : {candidates}
Determination : {determination}
Message ID : {message_id}
Run directory : {run_directory}
Body excerpt :
{body_excerpt}CSV-only fallback main content:
Please review the attached cases where the auto-responder could not determine the true sender email. Reply through the Multipub upload endpoint with outcome yes or no.Complete body (as sent) — intended routing for delivery note:
- Intended To:
NOTIFICATION_EMAIL_TARUN - Intended Cc: global Max + Vish
This message is notification catalog N03 — Tarun undetermined sender.
(See docs/NOTIFICATIONS_CATALOG.html)
{main content}
Attachments:
- tarun_undetermined_senders.csv: Cases where the true sender email could not be determined (Tarun review).
--- Delivery note ---
SMTP_NOTIFICATION_RECIPIENTS is active; this message was delivered to the override list.
Override To : {SMTP_NOTIFICATION_RECIPIENTS}
Intended To : {NOTIFICATION_EMAIL_TARUN}
Intended Cc : {NOTIFICATION_EMAIL_MAX}, {NOTIFICATION_EMAIL_VISH}Run-end notifications — N04–N08 (main.py)#
Call order: notify_marketing_suppression → notify_sai_action_items → notify_venu_cupola_audit_files → notify_run_audit_for_ip4 → notify_run_summary_for_leadership (log only).
N04 — notify_marketing_suppression#
| Field | Value |
|---|---|
| Catalog ID | N04 |
| Trigger | After output documents are written; skipped when no *_NoLongerThere_*.csv deliverable exists in the run directory or NOTIFICATION_EMAIL_ERIN is unset |
| To | NOTIFICATION_EMAIL_ERIN → shared marketing team mailbox (marketing-team-cbi@columbiabooks.com in .env / in-code default). Not Erin’s personal inbox — the env var name is historical. |
| Cc | Global Max + Vish only |
| Subject | [ACTION REQUIRED] [AutoResponder] Marketing suppression list - {run_dir.name} |
| Attachments | SFMC-ready suppression import CSV(s) matching *_NoLongerThere_*.csv only (written from inactive-people rows at end of run). output_document_inactive_people.csv stays on disk for internal ops and is not emailed. |
Here is the list of people that the auto responder marked as inactive (deceased, retired, left their organization, or no longer there).
Attached are suppression import files ready for SFMC ({filenames}).
Please suppress them from your marketing communications.
Run directory: {report_dir}Complete body (as sent) — intended routing for delivery note:
- Intended To: marketing team mailbox via
NOTIFICATION_EMAIL_ERIN(marketing-team-cbi@columbiabooks.com) - Intended Cc: global Max + Vish
This message is notification catalog N04 — Marketing suppression.
(See docs/NOTIFICATIONS_CATALOG.html)
Here is the list of people that the auto responder marked as inactive (deceased, retired, left their organization, or no longer there).
Attached are suppression import files ready for SFMC (Marketing_NoLongerThere_2026-05-27.csv).
Please suppress them from your marketing communications.
Run directory: {report_dir}
Attachments:
- Marketing_NoLongerThere_2026-05-27.csv: SFMC suppression import file (Email Address, Status, Date Added).
--- Delivery note ---
SMTP_NOTIFICATION_RECIPIENTS is active; this message was delivered to the override list.
Override To : {SMTP_NOTIFICATION_RECIPIENTS}
Intended To : marketing-team-cbi@columbiabooks.com (NOTIFICATION_EMAIL_ERIN)
Intended Cc : {NOTIFICATION_EMAIL_MAX}, {NOTIFICATION_EMAIL_VISH}N05 — notify_sai_action_items (replacements-only)#
| Field | Value |
|---|---|
| Catalog ID | N05 |
| Trigger | After output documents when output_document_alternate_contacts.csv has rows and inactive-no-cupola, inactive-new-org, and actionable HR counts are all zero (meeting notes §1.5). Skipped when Sai unset. |
| To | NOTIFICATION_EMAIL_SAI (skips cleanly when unset) |
| Cc | Global Max + Vish only |
| Subject | [ACTION REQUIRED] [AutoResponder] New replacements for inactive contacts - {run_dir.name} |
| Attachments | N05: output_document_alternate_contacts.csv only |
Body opening (fixed):
Please complete the manual Cupola actions below for this run.
Search Cupola for each person; if a record is missing, create it and associate it to the correct organization.
Reply when manual actions for this run are complete.
Run directory: {report_dir}Then up to four sections with per-record blocks:
- SECTION 1 —
output_document_alternate_contacts.csv: Original email,Subject,Message ID, replacement name/email/title, org (body blocks echo these fields). - SECTION 2 —
output_document_inactive_no_cupola_match.csv: contact email, person name, org, determination, source subject/message id. - SECTION 3 —
output_document_inactive_new_org.csv: contact email, person name, previous org, new org name/title, Cupola action, source subject/message id. - SECTION 4 — Human Review rows whose
Reasonis inSAI_ACTION_HUMAN_REVIEW_REASONS: active_new_contact_no_cupola, reactivation_candidate_inactive_cupola, email_or_title_update_on_inactive_cupola, replacement_parse_fallback, unknown_determination, no_contact_match.
Per-record block formats (in body when section has rows):
SECTION 1 - New replacements for inactive contacts
===============================================
Rows: {N}
{idx}. Original: {Email Received From}
Replacement Name : {Alternate Person Name}
Replacement Email: {Alternate Person Email}
...
SECTION 2 - Inactive contacts with no Cupola match (research)
{idx}. Contact Email : ...
SECTION 3 - Inactive contacts at a new organization
{idx}. Contact Email : ... New Org Name : ...
SECTION 4 - Other Human Review action items
{idx}. Contact Email : ... Reason : ... Notes : ... Suggested Action : ...Complete body (as sent) — N05 only (replacements-only); intended routing:
- Intended To:
NOTIFICATION_EMAIL_SAI - Intended Cc: global Max + Vish
- Typical attachments:
output_document_alternate_contacts.csvonly
This message is notification catalog N05 — New replacements for inactive contacts (Sai / IP4).
(See docs/NOTIFICATIONS_CATALOG.html)
Please complete the manual Cupola actions below for this run.
...
{SECTION 1 blocks only when N05}
Attachments:
- output_document_alternate_contacts.csv: Replacement contacts suggested for inactive subscribers.
--- Delivery note ---
Intended To : {NOTIFICATION_EMAIL_SAI}
Intended Cc : {NOTIFICATION_EMAIL_MAX}, {NOTIFICATION_EMAIL_VISH}N06 — notify_sai_action_items (manual review bundle)#
| Field | Value |
|---|---|
| Catalog ID | N06 |
| Trigger | Combined bundle when no-cupola, new-org, and/or actionable HR rows exist (meeting §1.6). The method returns without sending when none of the relevant CSV attachments exist. |
| To | NOTIFICATION_EMAIL_SAI (skips cleanly when unset) |
| Cc | Global Max + Vish only |
| Subject | [ACTION REQUIRED] [AutoResponder] Manual review action items - {run_dir.name} |
| Attachments | Every relevant section CSV that exists (normally non-empty in generated run folders): output_document_alternate_contacts.csv, output_document_inactive_no_cupola_match.csv, output_document_inactive_new_org.csv, and/or generated output_document_human_review_action_items.csv for actionable SECTION 4 rows. |
Same method, SECTION 1–4 layouts, and per-record formats as N05. Catalog banner and subject differ.
Complete body (as sent) — N06 (combined bundle); intended routing:
- Intended To:
NOTIFICATION_EMAIL_SAI - Intended Cc: global Max + Vish
- Attachments: every relevant section CSV that exists (alternate, no-cupola, new-org, and/or
output_document_human_review_action_items.csvwhen SECTION 4 has rows)
This message is notification catalog N06 — Manual review action items (Sai / IP4).
(See docs/NOTIFICATIONS_CATALOG.html)
{same opening paragraph and Run directory as N05}
{one or more SECTION 1–4 blocks with per-record detail}
Attachments:
- {each attached CSV with output-document description}
--- Delivery note ---
Intended To : {NOTIFICATION_EMAIL_SAI}
Intended Cc : {NOTIFICATION_EMAIL_MAX}, {NOTIFICATION_EMAIL_VISH}N07 — notify_venu_cupola_audit_files#
| Field | Value |
|---|---|
| Catalog ID | N07 |
| Trigger | After cupola_audit_log.csv is written every run (including zero-entry runs when file exists). json_path is accepted but never attached. |
| To | NOTIFICATION_EMAIL_VENU |
| Cc | Sai and Max when distinct from Venu; plus global Max + Vish |
| Subject | [ACTION REQUIRED] [AutoResponder] CUPOLA audit log ready for review - {entry_count} row(s) - {run_dir.name} |
| Attachments | cupola_audit_log.csv; cupola_audit_log_rollback_plan.csv when present; output_document_human_review.csv when present (metadata — actionable HR rows also go to Sai N05/N06) |
Main content (fixed opening + dynamic counts):
Please review the attached Cupola audit log for this run and reply by email when you have finished processing the records.
Run directory: {csv_path.parent}
Audit entries: {entry_count}
Human Review rows: {total} ← when output_document_human_review.csv exists
Human Review counts by reason: ← when HR CSV has rows
- {reason}: {count}
...
Human Review reason key:
- unknown_determination LLM returned UNKNOWN
- no_contact_match Actionable outcome, no contact in any system
- bounce_pending_rule Bounce awaiting the Max + Vish rule
- replacement_parse_fallback Replacement alt-contact failed to parse
- active_new_contact_no_cupola Active person is missing from CUPOLA
- reactivation_candidate_inactive_cupola CUPOLA row is inactive but email indicates active
- email_or_title_update_on_inactive_cupola Email/title update requested on inactive CUPOLA row
- out_of_office_tracked_separately Out-of-office response awaiting split-out processComplete body (as sent) — intended routing for delivery note:
- Intended To:
NOTIFICATION_EMAIL_VENU - Intended Cc:
NOTIFICATION_EMAIL_SAIandNOTIFICATION_EMAIL_MAXwhen distinct from Venu; plus global Max + Vish (Max may appear once after dedupe)
This message is notification catalog N07 — Venu CUPOLA audit.
(See docs/NOTIFICATIONS_CATALOG.html)
{main content above}
Attachments:
- cupola_audit_log.csv: CUPOLA changes applied or recommended during this run.
- cupola_audit_log_rollback_plan.csv: Rollback SQL for auto-applied CUPOLA status changes. ← when present
- output_document_human_review.csv: Human Review metadata for every HR row in the run. ← when present
--- Delivery note ---
Intended To : {NOTIFICATION_EMAIL_VENU}
Intended Cc : {Sai when ≠ Venu}, {Max when ≠ Venu}, {Max}, {Vish} (actual merge/dedupe in code)N08 — notify_run_audit_for_ip4#
| Field | Value |
|---|---|
| Catalog ID | N08 |
| Trigger | After processing reports, output documents, Cupola audit, classifier output, and batch_report.html are written. Returns false when no attachable files exist. |
| To | settings.ip4_audit_recipients() → NOTIFICATION_EMAIL_SAI |
| Cc | Global Max + Vish only |
| Subject | [AutoResponder] Run reports - {run_dir.name} (no [ACTION REQUIRED] prefix) |
| Attachments | Any present files after allowlist: processing_report.log (or configured report filename), processing_report_master.csv, processing_report_ip4.csv, category_summary_report.csv, batch_report.html. impact_report.txt is written on disk but not attached (impact metrics are inline in the body and in the log). |
Body sections (built in notifier.py):
- Run identity (directory; run started parsed from
run_YYYYMMDD_HHMMSSfolder name when possible) - Email pull window (UTC start/end) when backlog snapshot is available
- Impact summary with definitions (emails processed, records deactivated, records added)
- This-run progress (processed count + repository backlog estimate, or master-CSV complete vs pending)
- Category breakdown from
category_summary_report.csv - Processing status breakdown from master CSV
- IP4 ledger row count
- Repository backlog snapshot (or skipped/incomplete reason)
Main content (all sections optional except opening; built in notify_run_audit_for_ip4):
This email contains the run ledger and summary files for verification.
Run identity:
Run directory : {report_dir}
Run started : {YYYY-MM-DD HH:MM:SS UTC (from folder name)} ← when parseable
Email pull window (UTC): ← when backlog snapshot OK
Start : {ISO8601}
End : {ISO8601}
Impact summary (this run):
Run ID : {run_id}
Emails processed : {N} (messages handled in this batch)
Records deactivated : {N} (auto-applied CUPOLA inactive transitions)
Records added : {N} (successful contact_addition with contact_id)
This-run progress: ← backlog snapshot or master CSV
Processed this run : {N}
Repository backlog estimate : {N}
Category breakdown (category_summary_report.csv): ← when file exists
- {category}: {count}
Processing status breakdown (master CSV): ← when master exists
- {status}: {count}
IP4 ledger:
processing_report_ip4.csv rows : {N}
Repository backlog snapshot: ← when snapshot OK
Total candidates in window : {N}
Estimated still unprocessed : {N}
Oldest unprocessed ReceivedDate (UTC): {ISO8601 or (none - backlog empty)}
Days behind (UTC calendar vs now): {N}Complete body (as sent) — intended routing for delivery note:
- Intended To:
settings.ip4_audit_recipients()→NOTIFICATION_EMAIL_SAI - Intended Cc: global Max + Vish
- Note:
impact_report.txtis written on disk but is not attached to the email (not in allowlist attachment list for this method).
This message is notification catalog N08 — Run audit (IP4).
Review the attached run ledgers and summaries to verify this batch. Use processing_report_ip4.csv for IP4-actionable rows; no reply needed unless something looks wrong.
(See docs/NOTIFICATIONS_CATALOG.html)
{main content sections above}
Attachments:
- processing_report.log: Full per-email processing trace, run summary, and impact headline metrics.
- processing_report_master.csv: Ledger of every email in the run (full internal column set).
- processing_report_ip4.csv: IP4-actionable emails only (filtered categories, 23-column order).
- category_summary_report.csv: LLM category counts for the run.
- batch_report.html: HTML dashboard summarizing run outcomes.
(only files that exist on disk are listed)
--- Delivery note ---
Intended To : {NOTIFICATION_EMAIL_SAI}
Intended Cc : {NOTIFICATION_EMAIL_MAX}, {NOTIFICATION_EMAIL_VISH}Upload API — N09–N10 endpoint#
POST /multipub/upload — multipart file plus form outcome=yes|no (default yes). Implemented in tarun_upload_api.py.
N09 — notify_multipub_subscriber_followup_from_upload#
| Field | Value |
|---|---|
| Catalog ID | N09 |
| Trigger | outcome=yes after file is stored under TARUN_UPLOAD_DIR. The current upload endpoint passes no parsed items, so it normally uses the short “please review upload” body and original uploaded filename. |
| To | Angel + Yogesh (same as N02) |
| Cc | Matt; Tarun (receipt, when not already in To); global Max + Vish |
| Subject | With parsed items: [ACTION REQUIRED] [AutoResponder] Tarun confirmed subscriber match - Subscriber follow-up needed - {n} contact(s). Without items: … Tarun confirmed subscriber match - please review upload |
| Attachments | Uploaded file under original filename only if it survives the notification allowlist (.csv, .txt, .log, .html); optional inline multipub_subscriber_followup.csv from the payload builder when items are passed. Uploaded .tsv, .xls, and .xlsx files are accepted and audited by the API but are dropped from the notification email by the shared attachment filter. |
Main content — no parsed items (current Yes-path default):
Tarun reviewed an undetermined-sender case and confirmed a subscriber match in Multipub.
Please review the attached file and reach out to the customer or related contacts to make the necessary subscription and contact adjustments.
Note from Tarun: ← when upload form includes a note
{note text}This body text is fixed in code. If the uploaded file has a suffix accepted by the upload API but not by filter_notification_attachments (for example .xlsx), the email can still say “attached file” while the attachment is omitted.
Main content — with items: same per-contact blocks as N02 from build_multipub_subscriber_followup_payload (subject prefix adds Tarun confirmed subscriber match -).
Complete body (as sent) — intended routing for delivery note:
- Intended To: Angel + Yogesh (same as N02; Max fallback)
- Intended Cc: Matt when set;
NOTIFICATION_EMAIL_TARUNwhen not already in To; global Max + Vish - Attachments: uploaded file under original filename only when allowlisted; optional inline CSV when items passed
This message is notification catalog N09 — Tarun upload — Yes (subscriber follow-up).
(See docs/NOTIFICATIONS_CATALOG.html)
{main content — short message or N02-style blocks}
Attachments:
- {uploaded_filename}: Supporting file from this run (see filename). ← only if upload filename is allowlisted; or multipub CSV description
--- Delivery note ---
Intended To : {Angel}, {Yogesh}
Intended Cc : {Matt}, {Tarun when not in To}, {Max}, {Vish}N10 — append_tarun_upload_audit_row — no email#
outcome=no appends to {TARUN_UPLOAD_DIR}/tarun_upload_audit.csv with columns: timestamp, original_filename, outcome, uploaded_by, note. No notification is sent.
Action items tracker (N01–N07)#
After each run, main.py calls append_action_items_for_run(report_dir) to append one row per action notification email (N01–N07 that had attachments) to action_items_tracker.csv (default under processing_reports/; override ACTION_ITEMS_TRACKER_PATH). Each row has ActionItemCount, a file breakdown in Summary, and Completed=false until an operator marks it done. See DATA_DICTIONARY §32. Catalog N12 is a separate CLI follow-up (not appended to the tracker) that asks each N01–N07 notification owner to reply confirming work for that run.
Action items completion confirmation (N12)#
CLI: auto-responder-request-action-item-confirmation <processing_reports/run_*> (send_action_items_confirmation.py).
Reads the same artifacts as collect_action_item_detail_rows, groups by NotificationId (N01–N07), and sends one N12 email per group to the notification owner (Venu for N01/N07, Sai for N05/N06, Angel+Yogesh for N02, etc.). Does not inspect the Sent mailbox.
| Flag | Behavior |
|---|---|
--dry-run | Notifier(dry_run=True) — log only, no SMTP |
--test-email ADDR | Deliver To-only to ADDR; footer shows Intended To / Intended Cc for production routing (CLI override disclosure) |
--only N01,N06 | Limit to comma-separated notification IDs |
Subject: [ACTION REQUIRED] [AutoResponder] Confirm completion — {original N## title} — {run date}
Body: Reply request for the run date, reference to the original N## notification, row counts, breakdown by source file, and up to 25 item lines from tracker fields.
Resend from an output folder#
CLI: auto-responder-send-output-emails <processing_reports/run_*> (send_output_folder_emails.py).
| Flag | Behavior |
|---|---|
--dry-run | Notifier(dry_run=True) — log only, no SMTP |
--skip-queued | Skip batch notifications (N01–N03) emails (Hodor, Multipub follow-up, Tarun undetermined); send once-per-run bundle only |
Dispatch groups (in order):
- N01 hodor-prospect-import — loads rows from
hodor_prospect_import_verification.csv - N02 multipub-subscriber-followup — CSV-only resend when
multipub_subscriber_followup.csvexists - N03 tarun-undetermined-senders — CSV-only when
tarun_undetermined_senders.csvexists - N04 marketing-suppression — when
*_NoLongerThere_*.csvexists in the run folder - N05/N06 sai-action-items
- N07 venu-cupola-audit — rebuilds entry count from JSON or CSV
- N08 ip4-run-audit — restores
impact_reportand backlog fromprocessing_report.jsonwhen present
Leadership summary is not resent (not implemented).
Deferred (N11) / not implemented#
notify_run_summary_for_leadership#
Called from main.py after the IP4 run audit. Does not send email today. Builds a metrics-only preview body and logs at INFO:
AutoResponder run summary (metrics only)
Run directory: {report_dir}
Emails processed: …
Records deactivated: …
Records added: …
Email/title corrections (auto-applied): … (from cupola_audit_log.json when present)
Distinct organizations in contact additions: … (or "not tracked yet")Per meeting notes and IMPLEMENTATION_PLAN.html, the live send should go to NOTIFICATION_EMAIL_LEADERSHIP with no CSV attachments, only after Sai confirms action items and Venu confirms the audit log (reply email or API). Intended subject: [AutoResponder] Run summary - {run_dir.name}.
Preview body (log-only today) — uses the catalog banner but does not send SMTP:
This message is notification catalog N11 — Leadership run summary (deferred; not emailed).
(See docs/NOTIFICATIONS_CATALOG.html)
AutoResponder run summary (metrics only)
Run directory: {report_dir}
Emails processed: {N}
Records deactivated: {N}
Records added: {N}
Email/title corrections (auto-applied): {N} ← when cupola_audit_log.json present
Distinct organizations in contact additions: {N} ← or "Organizations added: not tracked yet"When implemented, intended delivery note would show Intended To : {NOTIFICATION_EMAIL_LEADERSHIP} and global Max + Vish in Intended Cc.
Legacy ID mapping#
Prior docs and meeting notes used tiered IDs (1.x, 2.x, 3.x). Use N01–N11 in new mail banners and references.
| Legacy | New | Notes |
|---|---|---|
| 1.1 | N02 | Multipub batch follow-up |
| 1.2 | N03 | Tarun undetermined sender |
| 1.3 | N01 | Hodor Prospect Import (flush order changed) |
| 2.1 | N08 | IP4 run audit |
| 2.2 | N05 or N06 | Replacements-only vs manual-review bundle (meeting §1.5 → N05, §1.6 → N06) |
| 2.3 | N07 | Venu CUPOLA audit |
| 2.4 | N04 | Marketing suppression |
| 3.1 | N09 | Tarun upload Yes |
| 3.2 | N10 | Tarun upload No (log only) |
| — | N11 | Leadership summary (deferred) |
Open follow-ups#
- Service mailbox: dedicated
cbi-automationsender (interim: global Max + Vish Cc on all notifications). - Hosting: bumblebee / AWS — access TBD.
- Leadership send + confirmation gate: implement inbox/API detection for Sai and Venu completion replies.