Job-application artifact · Built for Stylework · July 2026 · Shipped in ~51 hours

SitelineCRM: capture sales data
where the conversation already is.

A Telegram-native B2B sales CRM built for Stylework, an Indian coworking-space aggregator. A rep forwards a WhatsApp chat, sends a voice note, or shares a screenshot. No forms. The model extracts contact, company, seat count, city, space type, budget, move-in date, stage, and next action.

B2B SaaS AI / Extraction Telegram Bot Coworking / Real Estate Solo Build

The short
version.

At a glance
RoleSolo PM and builder. No engineering team.
WhenJuly 2026. About 51 hours from first commit to latest.
Built forStylework, an Indian coworking aggregator. A job-application artifact.
What shippedA Telegram-native sales CRM. A rep forwards a chat, sends a voice note or shares a screenshot, and the model fills the record. A dashboard and an eval harness came with it.
Result93.5% field accuracy (129/138) on 23 synthetic transcripts. Stage alone scored 14/23, or 60.9%.
LimitSynthetic data, demo-grade auth, no customers.
See itLive product ↗   GitHub ↗
Documentation
GitHub → Live Product →
THREE CAPTURE MODES → ONE SAVE PATH WhatsApp Forward Raw text Voice Note Whisper transcription Screenshot gpt‑4o‑mini vision Telegram Bot flows.py handlers AI Extraction OpenAI gpt-4o-mini Quality Gate Text captures only _save_capture() Find/create + log Confirmation Card “Looks good” / “Edit stage” or: intent = “recall” → brief instead of capture Save path never regresses a lead’s stage backwards from a vague recapture
SYSTEM ARCHITECTURE: BOT + API + DASHBOARD TELEGRAM BOT / RAILWAY AI NEON POSTGRES DASHBOARD Rep’s Chat Forward/voice/image FastAPI + PTB Single asyncio process gpt-4o-mini + Whisper (voice) asyncpg Fully async db.py /api/* Endpoints HMAC-signed tokens Dashboard SPA Static JS, GitHub Pages Space Matching City/seats hard filter Single OpenAI key for extraction, vision, and transcription. No separate provider accounts to manage

This page is the build record for SitelineCRM. It covers the data-capture problem, the extraction pipeline, the eval harness that measures it, and the honest state of what shipped versus what didn’t.

Save as PDF →

Project overview

Overview

SitelineCRM was built solo as a job-application artifact for an “AI Product Builder” role at Stylework, an Indian coworking-space aggregator. It is live. Landing at siteline.gauravg.dev, dashboard at /dashboard, source at github.com/argaur/siteline-crm.

~51 hrs First commit to latest. Solo, no engineering team
32 Commits
93.5% Field-level accuracy (129/138) on 23 synthetic transcripts. Stage alone: 14/23, 60.9%
3 Capture modes: text forward, voice note, screenshot

Built by

Gaurav Gupta. Solo build, first commit 2026-07-16 13:20, latest 2026-07-18 16:16.

Deals don’t die from bad selling.
They go cold.

01 · Problem

A 50-seat inquiry and a 10,000-seat managed-office RFP both start the same way. An unstructured conversation happens: WhatsApp, a call, a walk-in.

A human then has to re-key it into a CRM, chase it, and remember to follow up. It’s a data-capture problem. Today it’s solved with headcount.

60-70% CRM abandonment rate. Re-keying data is friction nobody sustains

Where deals actually die

Re-keying is the tax

Every conversation a rep has must be manually retyped into a CRM before it’s usable pipeline data. That retyping is where the process breaks down first.

The conversation is already structured, in someone’s head

Seat count, budget, move-in date, next action: a rep already knows all of it after the call. The CRM asks them to say it twice.

Nobody retyped it in time

Deals rarely die from a bad pitch. They go cold because the last conversation never made it into the system before the next one arrived.

Capture stays where the
conversation already is.

02 · Solution

No forms. A rep forwards a WhatsApp chat, sends a voice note, or shares a screenshot to a Telegram bot. The model extracts contact, company, seat count, city, space type, budget per seat, move-in date, stage, and next action from raw text, audio, or images.

Forwarded text

Rep forwards the WhatsApp thread as-is. ai.extract_from_text() pulls the structured fields, then a quality gate checks the extraction before it saves.

Voice note

Whisper transcribes, then intent classification routes it: “capture” extracts a new update, “recall” generates a spoken-style brief on the deal instead. There’s no quality gate. Voice goes straight through.

Screenshot

gpt-4o-mini vision reads a shared screenshot, whether it’s a proposal PDF page, a WhatsApp screenshot, or a form, the same way it would read the text version.

What’s actually built
and working.

03 · Capabilities

Pipeline & heat

7-stage pipeline: Inquiry → Qualified → Site Visit → Proposal → Negotiation → Closed-Won / Closed-Lost, self-updating from every capture.

Heat scoring is recomputed on every read, never stored: recency (60%), interaction count (15%), deal size (25%). A never-touched lead reads Cold by design. It cannot drift out of date, because it’s never written.

Nudges & matching

Stalled-deal nudges: the bot DMs the assigned rep when a lead goes quiet past a threshold.

Space matching: city and available seats are hard filters. The 0-100 score is budget fit (45) + space type (30) + capacity right-sizing (25). A 50-seat lead into a 60-seat space outscores the same lead into a 500-seat space.

Manager & dashboard

Manager (“Team”) view: team funnel, rep leaderboard, stalled-deal list. It’s role-gated server-side. Reps hitting these endpoints get HTTP 403.

Dashboard home: pipeline funnel, heat strip, site-visit radar with weather, inventory pressure, a “Needs a Human Today” panel, live activity feed, and city footprint.

Demo mode

A visitor who owns no leads reads the seeded pipeline behind a sample-data banner. The product demonstrates itself.

It’s read-only: cross-user writes still return 403, even in demo mode.

The extraction isn’t just demoed.
It’s measured.

04 · Eval Harness

bot/eval/score_extraction.py runs against bot/eval/dataset.jsonl: 23 hand-written labelled transcripts, including Hinglish and informal phrasing that matches real usage, not clean English.

93.5% Field-level accuracy: 129/138
6 × 23 = 138 scored fields (6 fields per transcript)
9 Misses, all one failure mode

What’s scored, and what’s deliberately not

Six fields per row are graded: stage (exact match against the 7-value enum) plus presence checks for contact_name, company, budget_signal, seat_count, and city.

Free-text fields, summary and next_action, are deliberately excluded. Grading prose needs a human or an LLM-judge, and that reintroduces the non-determinism the metric exists to avoid.

A --min-accuracy flag makes the harness usable as a CI gate or a tuning-loop stop condition.

Read the 93.5% with its scope. stage is the only exact-match field, and it scored 14 of 23, or 60.9%. The five presence-only fields scored 115 of 115, but a presence check confirms a value exists, not that it is correct. The pooled number hides the weakest field, and that is the field that decides where a deal sits in the pipeline. The set is 23 text transcripts, with no separate held-out set and no voice or screenshot cases.

A documented trust boundary

When tuning extraction, only the prompt in ai.py may be edited. Never the dataset or the scorer. Editing either of those to make the number go up would be reward-hacking the eval instead of improving extraction.

The known weakness

The model under-reads stage, defaulting to Inquiry where Qualified or unknown is correct. This was found by the eval and confirmed three independent ways.

All 9 misses are this one failure mode. It’s a prompt fix, deliberately not attempted under deadline.

No framework, no bundler,
one provider.

05 · Tech
LayerTechnologyDetails
Bot backendPython · FastAPI · python-telegram-botSingle asyncio process, deploys to Railway. FastAPI lifespan wires bot polling + nudge JobQueue + /api/* together.
DatabaseNeon Postgres · asyncpgFully async. Every db.* call is awaited. Five tables: users, companies, leads, interactions, spaces.
AI · extraction & visionOpenAI gpt-4o-miniText extraction, vision (screenshots), and reasoning. One provider, one key.
AI · transcriptionOpenAI WhisperVoice note transcription ahead of intent classification.
DashboardStatic HTML/JS · no framework, no bundlerDeploys to GitHub Pages via Actions. Authenticates against the bot’s /api/* endpoints via signed per-user HMAC tokens. No credentials shipped to the browser.

What this is,
and what it isn’t.

06 · Limitations

This candour is deliberate. A build story that hides its limitations is less useful than one that names them.

Runs on seeded data

The pipeline runs on seeded sample data, labelled in-product as such. It’s a working product, not a customer deployment. No revenue.

Auth is demo-grade

Signed HMAC tokens with server-verified roles, but no expiry or revocation. This is a deliberate one-day-demo simplification, not production-grade auth.

The eval dataset is synthetic

Hand-written rather than drawn from real bot traffic, so 93.5% is indicative of extraction quality, not field-proven against production usage.

Complexity the product earns,
not imposes.

07 · Where This Goes

Most CRMs fail because they impose enterprise process on a five-person sales team. SitelineCRM is designed to earn its complexity as the org around it grows. The rep’s own experience never gets heavier.

Org shapeWhat unlocks
Solo repThe bot, and nothing else: capture, pipeline, heat score, nudges.
Add repsAssignment and a leaderboard.
Add a managerTeam funnel and stalled-deal escalation, already built.
Add a sales head over multiple managersRegion roll-ups, forecasting, quota tracking.
CEO levelThe revenue view.

Structure accretes above the rep, not on top of them. That’s the thesis this build is arguing for.

Where it started:
Rethink CRM.

08 · Version 0

SitelineCRM began as Rethink CRM, a research concept for founders who sell on their own. Founder interviews showed the same pattern each time. They meant to use a CRM, logged by hand for a few days, and gave up by week four.

Rethink CRM bet that the chat is the record. It became a PRD and a scrappy Telegram bot for solo founders. It never reached a real team.

Rethink CRM (v0)SitelineCRM (v1)
UserA solo founder who sellsA sales team at Stylework, with reps and a manager
StageResearch, PRD and a rough botShipped and live on seeded data
ProofFounder interviewsAn eval harness that scores field extraction
KeptThe core bet: capture sales data where the conversation already is.