Lead-to-Payment — Master Sales Flow
Overview
This is the end-to-end pipeline for a service business: a lead comes in, the agent
turns it into a priced estimate, the owner approves it over Telegram, and the agent
creates a payment link or invoice and tracks it to paid. The owner is never bypassed —
the agent drafts and proposes, the human approves, then the agent executes. This
mirrors how a small business owner actually wants to work: they stay in control,
the agent removes the 30–60 min of manual work per estimate.
Environment assumptions
- Model:
zai-org/GLM-5.2 via Together AI, direct (no proxy).
- Gateway: Telegram, running as a systemd service. Tool calling works in the gateway.
- Secrets live in
~/.hermes/.env.
- Email: GoDaddy Workspace Email via IMAP/SMTP (
himalaya CLI, not Microsoft Graph).
Config at ~/.config/himalaya/config.toml, password at ~/.config/himalaya/.email-pw.
IMAP: imap.secureserver.net:993 TLS. SMTP: smtpout.secureserver.net:465 TLS.
NOT M365 — outlook-graph skill is kept as a separate skill for businesses that use real M365.
See references/godaddy-email-setup.md for full setup + verified commands.
- Payments: Stripe test mode (
STRIPE_SECRET_KEY in .env).
- QuickBooks is not wired (post-hackathon P1). The flow degrades gracefully without it.
When to Use
- A referral or customer inquiry arrives (e.g., 80% of leads are referral-based).
- Owner forwards/pastes a lead to the agent in Telegram.
- A website estimator form submission needs a real estimate + payment path.
- A voicemail monitor (
grasshopper-voicemail-monitor) logged a new lead and the
owner says "price it."
The Flow (happy path)
1. Intake
Lead arrives one of three ways:
- Email inbox (GoDaddy via
himalaya): owner says "check the email" → agent
runs himalaya envelope list to read the inbox, identifies leads vs spam/newsletters.
- Telegram (always works): owner pastes "new lead: Jane Doe, 2,400 sqft home in [CITY],
cigarette smoke, referral from [Referral Partner]."
- Website form (automated, cron-based): the site's contact form emails the owner's
inbox with subject
New website lead: ... and a structured body. A no_agent=True
cron job polls the inbox every 3 min, parses the fields, logs to CRM, and delivers a
formatted summary to the owner's Telegram. v2.0: this is wired and running —
see references/website-lead-intake.md for the polling architecture.
Agent extracts: customer name, contact (email/phone), property address + region,
square footage, property type (residential/commercial/apartment), severity/service type
(e.g. cigarette >5yrs, pet/trauma), lead source.
2. Summarize + notify owner (Telegram)
Agent posts a short summary to the owner:
📥 New lead — Jane Doe (referral, [Referral Partner])
2,400 sqft residential, [CITY] ([REGION]), cigarette smoke >5yrs
Want me to draft an estimate?
Wait for owner go-ahead before pricing. (Keeps a human gate at the top.)
3. Price it — estimator-engine
Run the estimator with the extracted details. Use the published rate card from
the estimator-engine config (do not invent prices). For 2,400 sqft standard region,
that's the 1,500–2,500 sf tier at the configured rate per psf plus the cigarette >5yrs
extended add-on. Return structured line items, not just a total.
4. Log to CRM — crm-lite
Upsert the customer and create a lead/estimate record:
customers: name, email, phone, address, city, state, zip, lead_source, lead_status=qualified
estimates: line items, total, status=draft, link back to customer id
5. Propose the estimate to the owner (Telegram) — APPROVAL GATE
Show the owner the full estimate and ask before sending:
🧾 Estimate for Jane Doe — $3,300
• Odor treatment, 2,400 sqft @ $1.25 = $3,000
• Cigarette >5yrs extended = +$550 (adjust per rate card tier)
• Travel surcharge ([REGION]) = +$75
Send this to the customer as a payment link / invoice? (yes / edit / no)
Do NOT send or charge anything until the owner says yes.
6. On approval — create the charge — stripe-payments
Default to a Stripe payment link (simplest, most demo-friendly). For NET-30 jobs
use a Stripe invoice instead. Amounts are in cents ($3,300 = 330000).
- Create/lookup Stripe customer (store metadata: region, sqft, source).
- Create payment link (or invoice → finalize → send).
- Capture the returned
https://buy.stripe.com/... URL.
7. Draft customer email — himalaya (IMAP/SMTP) — DRAFT ONLY, NO AUTO-SEND
Agent composes a customer-facing email from owner@yourdomain.com containing:
- The estimate summary (line items + total)
- The Stripe payment link
- Business contact info + call-to-action
The agent does NOT send this email automatically. It shows the full draft to the
owner in Telegram and asks: "Here's the draft email to [customer]. Want me to send it?"
The owner must explicitly say "send" / "yes" before the agent sends via himalaya.
If the owner says "edit" → agent revises and shows again.
If the owner says "no" → agent does not send, logs the decision in CRM.
To send after approval:
cat << 'EOF' | himalaya template send
From: owner@yourdomain.com
To: customer@example.com
Subject: Your [BUSINESS_NAME] Estimate
[Email body with estimate + payment link]
EOF
To save as draft in the owner's Drafts folder (for the owner to review/edit/send):
cat << 'EOF' | himalaya message save --folder Drafts
From: owner@yourdomain.com
To: customer@example.com
Subject: Your [BUSINESS_NAME] Estimate
[Email body with estimate + payment link]
EOF
Both paths keep the owner in control. Drafts live in the Drafts folder and sync
to the owner's desktop email client.
For email thread replies (when replying to an existing conversation, e.g. a
referral email): use himalaya template reply <message_id> to generate a
reply template with proper In-Reply-To and References headers, then pipe the
edited template through himalaya message save --folder Drafts (to save as draft)
or himalaya template send (to send after owner approval). Do NOT compose a fresh
email for a thread reply — it breaks the threading headers and the customer won't
see it as part of the conversation.
8. Update CRM + set follow-up
estimates.status = sent, store the Stripe link / invoice id.
- Create a follow-up reminder (todo or cron) for 3 days out if unpaid.
9. Track to paid
- Poll Stripe payment status (or receive a webhook if configured).
- On
paid: notify owners in Telegram ("✅ Jane Doe paid $3,300"), set
estimates.status = paid and customers.lead_status = won.
- If QuickBooks is wired: record the payment against the QBO invoice. If not, the Stripe
record is the source of truth — fine for now.
Current state (what runs TODAY)
- Intake: Telegram paste OR
himalaya inbox read (GoDaddy IMAP — wired and verified)
OR automated website-lead polling (v2.0 — wired; see references/website-lead-intake.md).
- Price: estimator-engine (no creds needed, rate tables in skill config).
- Log: crm-lite (SQLite, no creds needed).
- Charge: Stripe payment link (test mode key set).
- Draft email: himalaya IMAP/SMTP (GoDaddy — wired and verified). DRAFT ONLY — owner
approves before send. Use
himalaya message save --folder Drafts to save drafts (NOT
template save — that command does not exist). Use himalaya template reply <msg_id> for
thread replies to preserve In-Reply-To headers.
- Track: agent checks Stripe status on request.
QuickBooks is not wired (post-hackathon P1). The Stripe record is the source of truth.
Owner-in-the-loop rules (important)
- Always summarize a lead before pricing, and always show an estimate before sending.
- Never create a Stripe charge, send an email, or mark anything paid without explicit
owner approval in the chat. The agent proposes; the human commits.
- Keep Hermes command approvals ON. For a business that spends real money, annoying-but-safe
beats fast-but-loose.
Demo script
- Owner pastes a referral lead into Telegram.
- Agent summarizes it and asks to draft an estimate.
- Agent prices it with estimator-engine, logs to crm-lite, shows the estimate.
- Owner taps "yes."
- Agent creates a Stripe (test mode) payment link and returns it.
- On camera, pay with test card
4242 4242 4242 4242.
- Agent detects
paid, posts "✅ paid" in Telegram, updates the CRM.
Pitfalls
- Stripe amounts are in cents. $3,300 = 330000, not 3300.
- Use Stripe test mode (
sk_test_…) for the demo; test cards only work in test mode.
- Match estimate totals to the estimator-engine output exactly so the charge equals the quote.
- Don't claim QuickBooks is live if it isn't wired — present it as roadmap.
- Severity add-on amounts vary by sqft tier — pull the exact figure from the estimator rate card.
- Email is GoDaddy IMAP/SMTP via himalaya, NOT Microsoft Graph. Do not use
outlook-graph
skill for GoDaddy-hosted email — it's kept as a separate skill for other businesses that use real M365.
- Never auto-send customer emails. Always draft, show the owner in Telegram, get explicit
"send" approval, then send via himalaya.
- Region classification must match across all locations:
estimator-engine/SKILL.md
Location Tiers → the website estimator widget → the shared rate-card file. If you update
one, update all three. Canonical source is estimator-engine.
- Link CLI approval holds expire in ~6 minutes. When filming + approving real-money
flows, set up the camera first, then trigger the spend request. Don't race the timer.
- One-time virtual cards don't work for recurring subscriptions. The Link CLI issues
one-time-use cards; the first charge works but rebilling will fail. Accepted for demo;
handle recurring billing properly post-hackathon.
- Not all inbound emails are estimate requests. Some are brochure/info requests,
partnership inquiries, or vendor correspondence. Do NOT run these through the estimate
pipeline. Classify first: marketing/brochure requests → route to the marketing owner;
other non-estimate inbound → ask the owner for direction. Use
himalaya message forward
or a fresh email only when thread headers don't matter.
- Website-lead emails must match the parser. The form's subject prefix, field names,
and "phone or email" validation must stay in sync with the inbox monitor script. A
validation change on the site is a parser change too (see
references/website-lead-intake.md).
1---2name: lead-to-payment3description: Use when a new lead arrives (email, Telegram message, or website form) and needs to move through the full pipeline: qualify the lead, generate an estimate, get owner approval, create a payment link, draft a customer email for owner review, and track payment. This is the master flow that orchestrates estimator-engine, crm-lite, stripe-payments, himalaya (IMAP/SMTP), and quickbooks-online. Owner stays in the loop and approves before anything is sent or charged.4license: MIT5---67# Lead-to-Payment — Master Sales Flow89## Overview10This is the end-to-end pipeline for a service business: a lead comes in, the agent11turns it into a priced estimate, the owner approves it over Telegram, and the agent12creates a payment link or invoice and tracks it to paid. The owner is never bypassed —13the agent **drafts and proposes, the human approves**, then the agent executes. This14mirrors how a small business owner actually wants to work: they stay in control,15the agent removes the 30–60 min of manual work per estimate.1617## Environment assumptions18- Model: `zai-org/GLM-5.2` via Together AI, **direct** (no proxy).19- Gateway: Telegram, running as a systemd service. Tool calling works in the gateway.20- Secrets live in `~/.hermes/.env`.21- **Email:** GoDaddy Workspace Email via IMAP/SMTP (`himalaya` CLI, not Microsoft Graph).22 Config at `~/.config/himalaya/config.toml`, password at `~/.config/himalaya/.email-pw`.23 IMAP: `imap.secureserver.net:993` TLS. SMTP: `smtpout.secureserver.net:465` TLS.24 NOT M365 — `outlook-graph` skill is kept as a separate skill for businesses that use real M365.25 See `references/godaddy-email-setup.md` for full setup + verified commands.26- **Payments:** Stripe test mode (`STRIPE_SECRET_KEY` in `.env`).27- QuickBooks is not wired (post-hackathon P1). The flow degrades gracefully without it.2829## When to Use30- A referral or customer inquiry arrives (e.g., 80% of leads are referral-based).31- Owner forwards/pastes a lead to the agent in Telegram.32- A website estimator form submission needs a real estimate + payment path.33- A voicemail monitor (`grasshopper-voicemail-monitor`) logged a new lead and the34 owner says "price it."3536## The Flow (happy path)3738### 1. Intake39Lead arrives one of three ways:40- **Email inbox** (GoDaddy via `himalaya`): owner says "check the email" → agent41 runs `himalaya envelope list` to read the inbox, identifies leads vs spam/newsletters.42- **Telegram** (always works): owner pastes "new lead: Jane Doe, 2,400 sqft home in [CITY],43 cigarette smoke, referral from [Referral Partner]."44- **Website form** (automated, cron-based): the site's contact form emails the owner's45 inbox with subject `New website lead: ...` and a structured body. A `no_agent=True`46 cron job polls the inbox every 3 min, parses the fields, logs to CRM, and delivers a47 formatted summary to the owner's Telegram. **v2.0: this is wired and running** —48 see `references/website-lead-intake.md` for the polling architecture.4950Agent extracts: customer name, contact (email/phone), property address + region,51square footage, property type (residential/commercial/apartment), severity/service type52(e.g. cigarette >5yrs, pet/trauma), lead source.5354### 2. Summarize + notify owner (Telegram)55Agent posts a short summary to the owner:56> 📥 New lead — Jane Doe (referral, [Referral Partner])57> 2,400 sqft residential, [CITY] ([REGION]), cigarette smoke >5yrs58> Want me to draft an estimate?5960Wait for owner go-ahead before pricing. (Keeps a human gate at the top.)6162### 3. Price it — `estimator-engine`63Run the estimator with the extracted details. Use the published rate card from64the estimator-engine config (do not invent prices). For 2,400 sqft standard region,65that's the 1,500–2,500 sf tier at the configured rate per psf plus the cigarette >5yrs66extended add-on. Return structured line items, not just a total.6768### 4. Log to CRM — `crm-lite`69Upsert the customer and create a lead/estimate record:70- `customers`: name, email, phone, address, city, state, zip, `lead_source`, `lead_status=qualified`71- `estimates`: line items, total, `status=draft`, link back to customer id7273### 5. Propose the estimate to the owner (Telegram) — APPROVAL GATE74Show the owner the full estimate and ask before sending:75> 🧾 Estimate for Jane Doe — **$3,300**76> • Odor treatment, 2,400 sqft @ $1.25 = $3,00077> • Cigarette >5yrs extended = +$550 (adjust per rate card tier)78> • Travel surcharge ([REGION]) = +$7579> Send this to the customer as a payment link / invoice? (yes / edit / no)8081Do NOT send or charge anything until the owner says yes.8283### 6. On approval — create the charge — `stripe-payments`84Default to a **Stripe payment link** (simplest, most demo-friendly). For NET-30 jobs85use a Stripe invoice instead. Amounts are in **cents** ($3,300 = 330000).86- Create/lookup Stripe customer (store metadata: region, sqft, source).87- Create payment link (or invoice → finalize → send).88- Capture the returned `https://buy.stripe.com/...` URL.8990### 7. Draft customer email — `himalaya` (IMAP/SMTP) — DRAFT ONLY, NO AUTO-SEND91Agent composes a customer-facing email from `owner@yourdomain.com` containing:92- The estimate summary (line items + total)93- The Stripe payment link94- Business contact info + call-to-action9596**The agent does NOT send this email automatically.** It shows the full draft to the97owner in Telegram and asks: "Here's the draft email to [customer]. Want me to send it?"98The owner must explicitly say "send" / "yes" before the agent sends via himalaya.99100If the owner says "edit" → agent revises and shows again.101If the owner says "no" → agent does not send, logs the decision in CRM.102103To send after approval:104```bash105cat << 'EOF' | himalaya template send106From: owner@yourdomain.com107To: customer@example.com108Subject: Your [BUSINESS_NAME] Estimate109110[Email body with estimate + payment link]111EOF112```113114To save as draft in the owner's Drafts folder (for the owner to review/edit/send):115```bash116cat << 'EOF' | himalaya message save --folder Drafts117From: owner@yourdomain.com118To: customer@example.com119Subject: Your [BUSINESS_NAME] Estimate120121[Email body with estimate + payment link]122EOF123```124125Both paths keep the owner in control. Drafts live in the Drafts folder and sync126to the owner's desktop email client.127128**For email thread replies** (when replying to an existing conversation, e.g. a129referral email): use `himalaya template reply <message_id>` to generate a130reply template with proper `In-Reply-To` and `References` headers, then pipe the131edited template through `himalaya message save --folder Drafts` (to save as draft)132or `himalaya template send` (to send after owner approval). Do NOT compose a fresh133email for a thread reply — it breaks the threading headers and the customer won't134see it as part of the conversation.135136### 8. Update CRM + set follow-up137- `estimates.status = sent`, store the Stripe link / invoice id.138- Create a follow-up reminder (todo or cron) for 3 days out if unpaid.139140### 9. Track to paid141- Poll Stripe payment status (or receive a webhook if configured).142- On `paid`: notify owners in Telegram ("✅ Jane Doe paid $3,300"), set143 `estimates.status = paid` and `customers.lead_status = won`.144- If QuickBooks is wired: record the payment against the QBO invoice. If not, the Stripe145 record is the source of truth — fine for now.146147## Current state (what runs TODAY)148- Intake: Telegram paste OR `himalaya` inbox read (GoDaddy IMAP — wired and verified)149 OR automated website-lead polling (v2.0 — wired; see `references/website-lead-intake.md`).150- Price: estimator-engine (no creds needed, rate tables in skill config).151- Log: crm-lite (SQLite, no creds needed).152- Charge: Stripe payment link (test mode key set).153- Draft email: himalaya IMAP/SMTP (GoDaddy — wired and verified). DRAFT ONLY — owner154 approves before send. Use `himalaya message save --folder Drafts` to save drafts (NOT155 `template save` — that command does not exist). Use `himalaya template reply <msg_id>` for156 thread replies to preserve In-Reply-To headers.157- Track: agent checks Stripe status on request.158QuickBooks is not wired (post-hackathon P1). The Stripe record is the source of truth.159160## Owner-in-the-loop rules (important)161- Always summarize a lead before pricing, and always show an estimate before sending.162- Never create a Stripe charge, send an email, or mark anything paid without explicit163 owner approval in the chat. The agent proposes; the human commits.164- Keep Hermes command approvals ON. For a business that spends real money, annoying-but-safe165 beats fast-but-loose.166167## Demo script1681691. Owner pastes a referral lead into Telegram.1702. Agent summarizes it and asks to draft an estimate.1713. Agent prices it with estimator-engine, logs to crm-lite, shows the estimate.1724. Owner taps "yes."1735. Agent creates a Stripe (test mode) payment link and returns it.1746. On camera, pay with test card `4242 4242 4242 4242`.1757. Agent detects `paid`, posts "✅ paid" in Telegram, updates the CRM.176177## Pitfalls178- Stripe amounts are in **cents**. $3,300 = 330000, not 3300.179- Use Stripe **test mode** (`sk_test_…`) for the demo; test cards only work in test mode.180- Match estimate totals to the estimator-engine output exactly so the charge equals the quote.181- Don't claim QuickBooks is live if it isn't wired — present it as roadmap.182- Severity add-on amounts vary by sqft tier — pull the exact figure from the estimator rate card.183- **Email is GoDaddy IMAP/SMTP via himalaya, NOT Microsoft Graph.** Do not use `outlook-graph`184 skill for GoDaddy-hosted email — it's kept as a separate skill for other businesses that use real M365.185- **Never auto-send customer emails.** Always draft, show the owner in Telegram, get explicit186 "send" approval, then send via himalaya.187- **Region classification must match across all locations:** `estimator-engine/SKILL.md`188 Location Tiers → the website estimator widget → the shared rate-card file. If you update189 one, update all three. Canonical source is `estimator-engine`.190- **Link CLI approval holds expire in ~6 minutes.** When filming + approving real-money191 flows, set up the camera first, then trigger the spend request. Don't race the timer.192- **One-time virtual cards don't work for recurring subscriptions.** The Link CLI issues193 one-time-use cards; the first charge works but rebilling will fail. Accepted for demo;194 handle recurring billing properly post-hackathon.195- **Not all inbound emails are estimate requests.** Some are brochure/info requests,196 partnership inquiries, or vendor correspondence. Do NOT run these through the estimate197 pipeline. Classify first: marketing/brochure requests → route to the marketing owner;198 other non-estimate inbound → ask the owner for direction. Use `himalaya message forward`199 or a fresh email only when thread headers don't matter.200- **Website-lead emails must match the parser.** The form's subject prefix, field names,201 and "phone or email" validation must stay in sync with the inbox monitor script. A202 validation change on the site is a parser change too (see `references/website-lead-intake.md`).