An AI pipeline for inbound leads: research each company with Exa, score it against your ICP with Claude, log everything to a Google Sheet, and drop a personalized outreach draft in Gmail.
Built on One. Nothing is ever sent automatically.
Give it a lead (or a CSV of leads) and for each one it:
- Researches the company with Exa: what they do, recent news, funding, launches.
- Scores the lead 1-10 against your ICP (you describe it in plain English in
config/icp.md) using Claude, with a written rationale and talking points. - Logs the row to a Google Sheet your whole team can see: score, rationale, talking points, sources.
- Drafts the outreach email in your Gmail drafts folder, personalized with a concrete detail from the research. It never sends; you review and hit send.
All three platform calls (Exa, Sheets, Gmail) go through One, so there is exactly one auth setup and one SDK instead of three API integrations.
$ npm start -- leads.example.csv
Processing 3 leads
── jane@ramp.com
researched ramp.com: 5 sources
fit 7/10: Ramp is a fintech building AI agents into their product...
gmail draft created (r880107...)
logged to sheet
1. Connect your accounts through One
npm i -g @withone/cli
one init # create an account or log in
one add exa
one add google-sheets
one add gmail2. Configure this repo
git clone https://github.com/withoneai/gtm-lead-agent
cd gtm-lead-agent
npm install
cp .env.example .envFill in .env:
ONE_SECRET: your One API key (in~/.one/config.jsonafterone init, or the dashboard)ANTHROPIC_API_KEY: from platform.claude.com- The three connection keys: run
one --agent listand copy thekeyfor exa, google-sheets, and gmail
3. Create your lead log sheet
npm run setupThis creates a spreadsheet with the right headers and prints the LEADS_SHEET_ID line to paste into .env.
4. Describe your ICP
Edit config/icp.md. It ships with an example; replace it with your product, your ideal customer, and your email voice. The scoring model reads this file verbatim, so plain English is exactly right.
# a CSV with an email column (name, company, notes optional)
npm start -- leads.csv
# a single lead
npm start -- --email jane@acme.com --name Jane --notes "asked about pricing"
# see the assessment and draft without writing anything
npm start -- --email jane@acme.com --dry-runBehavior worth knowing:
- Drafts are only created for leads scoring 5+. Everything is logged to the sheet either way.
- Leads on free email providers (gmail.com etc.) with no company name skip research and cap at 4/10.
--dry-runprints the score and the drafted email to the terminal and writes nothing.
| Step | Platform | How |
|---|---|---|
| Research | Exa | @withone/sdk typed action exa.search |
| Scoring + drafting | Claude (claude-opus-5) |
@anthropic-ai/sdk structured outputs (Zod schema) |
| Lead log | Google Sheets | @withone/sdk googleSheets.appendValuesSpreadsheetRange |
| Outreach draft | Gmail | @withone/sdk gmail.createUsersDraft |
~400 lines of TypeScript in src/. The One SDK handles auth, request signing, and retries for every platform call; swapping Sheets for HubSpot or adding Slack notifications is a few lines because the SDK types 30+ platforms the same way.
- Different CRM:
@withone/sdkships typed actions for HubSpot, Airtable, Notion, and more. Replacesrc/sheet.ts. - Trigger on form submits: wire your form webhook to run
npm start -- --email .... - Slack alerts for hot leads: after scoring, post 8+ leads to a channel via the One Slack actions.
MIT