Toosi
An autonomous outbound platform, built 0 to 1.
Toosi finds the right businesses, writes and sends personalised multi-step outreach from the client's own inbox, reads every reply with an LLM, and only bills when a prospect responds positively. Designed, built and shipped from a blank repo to real revenue.
- Role
- Senior Software Engineer · sole builder
- Timeline
- Jul 2023 — present
- Clients
- 2 — HQ'd in London, UK & Bengaluru, India
- Campaigns live
- US · UK · Ireland
2
Paying clients
Restaurants & hospitality
3
Markets live
US, UK and Ireland campaigns
$50
Per positive lead
Outcome-based pricing
1
Builder: me
Product, code, infra, sales
01The problem
Small teams don't want another tool. They want qualified replies.
Small B2B teams selling into local-business verticals spend most of their week on prospecting and follow-up, not on conversations. Existing tools either stop at "send a sequence" or charge a flat SaaS fee regardless of whether anything comes back.
That reframed the product. Toosi is not a sequencer. It is an outbound engine with an outcome-based price tag.
I don't want another tool. I want qualified replies in my inbox, and I want to pay for those.
02My role
Every layer, explicitly.
No hand-waving about “contributed to”. These are the parts I designed, built and operated.
- Product
- Requirements, outcome-based pricing, onboarding, dispute flow.
- Product architecture
- Multi-tenant boundaries, data model with 100+ entities, module layout.
- AI / agent design
- Scout, pitch and reply agents, playbooks, model routing, evaluation gates.
- Backend architecture
- NestJS services, durable agent runtime, conversation state machine, queues.
- Frontend
- React 19 workspace: sequences, conversations, billing and disputes.
- Infrastructure
- AWS deployment, RDS, SES, S3, PM2, key rotation, release gate.
- Go-to-market
- Client onboarding and the live campaigns in the US, UK and Ireland.
03Pricing model
Price the outcome, not the seat.
Charging on results forced the billing engine to be wired directly into the reply-classification pipeline, which is the most interesting engineering constraint in the system.
- Step 1$50Starting creditEvery workspace starts funded, so the first positive lead is free.
- Step 2$50Per positive leadCharged only when a prospect shows interest, asks for pricing or requests a meeting. Nothing else is billed.
- Step 3−$50Dispute, credited backDisagree with a classification? Dispute it in-app and the charge returns to the balance.
04What it does
Describe who you sell to, connect your inbox, and Toosi does the rest.
- 01Discoverbusinesses from an ICP
- 02Enrichdecision-makers
- 03Writethe pitch
- 04Sendfrom your Gmail / Outlook
- 05Classifyevery reply
- 06Hand offthe warm ones
Scout
Builds lead lists from an ICP description. Pulls businesses from web search providers, enriches decision-makers from LinkedIn and CRM imports, and stores a per-lead snapshot used for personalisation.
- Tavily
- SerpAPI
- DuckDuckGo
- Apollo
- HubSpot
Sequences
Multi-step programmes with per-step time windows, day-of-week rules, timezone-aware scheduling across three countries, thread-vs-new-thread strategy, and templated, agentic or hybrid generation.
Pitch agent
First-touch and follow-up emails grounded in a structured Playbook: social proof, claims, case studies, CTAs, tone, compliance rules and objection handling.
Reply agent
Classifies every inbound reply, pauses or hands off the conversation, drafts a suggested response, and fires the positive-lead billing event.
- interested
- meeting
- question
- objection
- referral
- OOO
- not interested
- unsubscribe
Deliverability
Sender pools with rotation, per-sender daily caps, warm-up tracking, bounce-pattern detection and suppression lists, all before a message is queued.
Conversation state machine
Every lead has a conversation with policy: auto-pause on reply, limits, handoff keywords, AI-reply thresholds. The system never double-sends or follows up over a human.
Billing
Stripe-backed ledger with credits, positive-lead charges, billing periods, PDF invoices, disputes and webhook idempotency.
Agent runtime
A durable task runtime with runs, steps, checkpoints, approvals and a policy guard. Long-running work survives restarts and can pause for a human.
Skills, memory, evaluation
Versioned skills registry, workspace memory with write policies, and an evaluation suite with a regression gate before any prompt or skill goes live.
Model routing
Capability-based model registry and router across OpenAI, Anthropic and local Ollama, with a compiled context layer and tool-exposure control.
05Architecture
TypeScript end to end. One codebase, and I own all of it.
A multi-tenant NestJS API in front of PostgreSQL and Redis, with queues for sends and research, OAuth into clients' inboxes, and a model router across three LLM providers.
Client
React 19 + Vite + Tailwind
TypeScript · TanStack Query · Auth0 JWT
API
NestJS API
- Workspaces · Users · Members
- Scout → Leads → People → Businesses
- Sequences → Campaigns → Emails
- Conversations · Reply Analyzer · Reply Agent
- Billing · Stripe ledger · Disputes
- Agent Runtime · Checkpoints · Approvals
- Skills · Tools · Memory · Evaluation
- Model router · Context compiler
PostgreSQL
TypeORM · 103 entities · migrations
Redis + BullMQ
send, research and runtime queues
Gmail · Outlook · SES
OAuth, SMTP, Google Calendar
OpenAI · Anthropic · Ollama
routed by capability
Tavily · SerpAPI · Apollo
HubSpot, Twilio, DuckDuckGo
- TypeScript
- NestJS
- TypeORM
- PostgreSQL / RDS
- Redis
- BullMQ
- React 19
- Vite
- TanStack Query
- Tailwind
- Auth0
- Stripe
- AWS SES / S3
- PM2
06Skills used
Everything the build touched, by layer.
AI & agents
- Agent orchestration
- LLM reply classification
- Structured outputs & tool calling
- Prompt & playbook design
- Model routing (OpenAI, Anthropic, Ollama)
- Context compilation
- Skills registry & workspace memory
- Evaluation suites & regression gates
- Human-in-the-loop approvals
Backend
- TypeScript
- NestJS
- TypeORM
- REST API design
- Multi-tenant architecture
- Auth0 / JWT
- Durable task runtime
- State machines
- Webhook idempotency
Data & queues
- PostgreSQL (AWS RDS)
- Schema design, 100+ entities
- Migrations-only schema changes
- Redis
- BullMQ job queues
- Send, research & runtime workers
- Rate limiting & scheduling
Frontend
- React 19
- Vite
- TanStack Query
- Tailwind CSS
- Multi-step workflow UIs
- Billing & dispute flows
Integrations
- Gmail & Outlook OAuth
- Google Calendar
- SMTP & Amazon SES
- Stripe billing & invoices
- Tavily, SerpAPI, DuckDuckGo
- Apollo & HubSpot imports
- Twilio
Infrastructure & deployment
- AWS deployment
- AWS RDS (PostgreSQL)
- AWS SES & S3
- PM2 process management
- Encrypted OAuth tokens & key rotation
- Credential-rotation checklist
- tsc / eslint / jest / build gate
Product & go-to-market
- Outcome-based pricing design
- Client onboarding
- Campaign operations (US, UK, IE)
- Deliverability strategy
- Dispute & trust flows
07Hard problems
Where the engineering actually lived.
- 01
Billing on an LLM's judgement
Charging $50 on a classifier's output is risky in both directions. The charge is idempotent per deal, the intent and evidence are logged on the ledger line, a dispute path credits the balance back, and the reply agent is tuned toward precision over recall for positive intents. Clients see exactly why they were charged.
- 02
Sending from the client's inbox without burning it
Deliverability is the product. Sender pools, per-sender daily caps, warm-up tracking, bounce-pattern detection and suppression checks all run before a message is queued. Three-country campaigns meant timezone-correct send windows per lead, not per campaign.
- 03
Never talking over a human
Once a prospect replies, the state machine pauses the sequence, routes to the rep and blocks further automated sends. Gmail and Outlook thread IDs are reconciled so replies land on the right conversation.
- 04
Making agent work durable
Research and enrichment can take minutes and call dozens of tools. The runtime persists runs, steps and checkpoints, supports approvals that pause for a human, and resumes after a restart instead of starting over.
- 05
Shipping alone, safely
Migrations-only schema changes against a remote RDS, encrypted OAuth tokens with a rotation script, a credential-rotation checklist, and a green tsc / eslint / jest / nest build gate before anything hits production.
08Engineering decisions
Why it is built this way.
Why PostgreSQL as the system of record instead of a vector database?
Almost everything Toosi stores is relational and transactional: leads, conversations, sequence steps, ledger lines, disputes. Personalisation runs off a structured per-lead snapshot and a structured playbook, not similarity search, so a vector store would have added a second source of truth for little gain. PostgreSQL with TypeORM and migrations-only schema changes keeps billing idempotency and the state machine in one consistent place.
Why BullMQ for asynchronous execution?
Research, enrichment and sends take seconds to minutes and must not live inside an API request. BullMQ on Redis gives delayed jobs for per-lead send windows, concurrency limits per sender pool, retries with backoff and separate queues for send, research and the agent runtime, so a slow research job never delays a scheduled email.
How does agent state survive failures?
The runtime persists every run, step and checkpoint in PostgreSQL. A worker restart resumes from the last checkpoint instead of starting over, and a step that needs a human parks the run in an approval state until someone acts. Tool calls that already completed are not replayed.
How is memory different from workflow state?
Workflow state is scoped to a run: inputs, step outputs, checkpoints, approvals. It is written only by the runtime. Memory is scoped to the workspace: entity intelligence, learned facts, preferences. It outlives any run, has explicit write policies on what an agent may store, and is read by agents as context rather than mutated mid-run.
How are agents prevented from taking unsafe actions?
Layered guards rather than a single prompt. The model router controls which tools each agent can see. The runtime's policy guard checks every step before execution. Conversation policy auto-pauses on any reply, enforces send limits and handoff keywords. Suppression lists and per-sender caps run before a message is queued. Anything flagged waits for human approval.
How do you charge on a classifier's output and stay fair?
The positive-lead charge is idempotent per deal, the intent and the evidence are written on the ledger line, and the reply agent is tuned for precision over recall on positive intents. If a client disagrees, a dispute credits the balance back. The client can always see why they were charged.
09What I'd tell another builder
Price the outcome, not the seat.
Performance pricing forced every engineering decision to serve the client's actual goal. It was the single biggest reason the first two clients signed.
Deliverability before intelligence.
The smartest email is worthless in spam. Sender pools and suppression shipped before the pitch agent did.
Put the human in the loop at the right moment.
Autonomous up to the reply, then hand off. That boundary is what clients trust.
10Ownership
I conceived, designed, coded, deployed and brought everything here to market.
- Product requirements
- Data model
- Backend services
- Frontend
- Infrastructure
- Stripe integration
- Client onboarding
- The campaigns themselves
Contribution
- Product & pricing design
- System architecture
- Agent orchestration
- LLM reply classification
- Email deliverability
- Stripe billing & disputes
- Full-stack development
- Cloud infrastructure
- Go-to-market