Tools · Leads ← Tools menu Updated 11-09-2026

Leads

One row per lead, one column per day. E1 is the first email, E2 the first follow-up. The last column says whether the lead became a customer.
Loading…
For Claude

Sources: D1 leads (migration 0019, one row per Notion prospect keyed by Lead number), email_sends (written by byforja-backup-worker at every send), contacts plus client_contract for the customer flag.

Join: email_sends.prospect_page = leads.notion_page_id, dashes ignored. A send whose page is not in leads is listed as unmatched, never guessed by email. Touch to ordinal: T1 = E1, T2 = E2, T3 = E3, T4 = E4, T5 = E5. TEST rows are dropped.

The sync skips and reports a Notion page whose Lead number is carried by two pages, or moved to another number, or would replace the page of a customer row (planSync in leads-core). The row keeps its last state; fix the number in Notion, then sync again.

Day of a cell = the UTC calendar day of sent_at. Columns run from the first send day in email_sends to today plus 350 days. Filter = the lead's Ligne in Notion.

Customer acquired = leads.customer_acquired = 1 (set by hand through the last column, POST /api/admin/leads-acquire) or the lead's email in contacts with has_contract = 1 or has_purchase = 1. The ordinal shown is the last E<n> sent before acquired_at. Yes links to /clients-detail when the email is a contact, else to /clients.

Endpoints: GET /api/admin/leads (this grid), POST /api/admin/leads-sync (Notion to D1 upsert, the Sync button), POST /api/admin/leads-acquire. Shell equivalent of the sync: node scripts/leads-sync-local.js. Logic in functions/_lib/leads-core.js.

Nothing here sends an email. The worker sends; this page only reads its log.