← back to all work

Call intelligence for three teams

Every sales, expert and finance call is transcribed, joined to the CRM, scored by an LLM against a rubric built on real calls, and turned into coaching for the person who made it. 17,000+ calls scored so far.

RoleArchitect and lead engineer
TimelineJun 2026 to present
StatusIn production
ContextBuilt at Oneday, an online education company selling degree programmes to US founders. One funnel, seven stages, five teams. I own the revenue numbers for the business and write the software that runs it. Three teams take calls on the way to a sale: salespeople, the experts who run the second interview, and the finance team who close.
TypeScript on DenoSupabase PostgresClaude APIFireflies transcriptsJustCall dialerCalendlyBitrix24 CRMGoogle Sheets APIGoogle Docs API
17,348calls scored · 8,310 sales · 7,694 expert · 1,344 finance
97people covered · 12 sales reps, 80 experts, 5 finance
4,809lead scores produced from expert calls
12.4% vs 0%win rate, top-tier vs bottom-tier lead score · 784 calls

01 — The problemManagers listened to a handful of calls a month and guessed about the rest.

Thousands of calls a month, three teams, one or two managers each. Coaching happened on whichever call a manager happened to join. Nobody could say which rep skipped discovery, which expert talked for 80% of the call, or whether the lead score a mentor typed into the CRM meant anything.

It did not. When I re-scored about 3,000 historical expert calls against a fixed rubric, the hand-entered score disagreed with the rubric tier on 65% of them. The score was retired.

02 — What I builtOne scoring engine, three cockpits.

PipelineTranscript sweepMetadata is swept from the transcript provider in capped windows into a cache, deduplicated by id. A transcript is matched to a lead only on positive identification: email, title phrase or participant name. Never closest-in-time.
PipelineScorerAn LLM scores each call against a team-specific rubric and returns structured fields: skill scores, behaviours hit, red flags, what went well, what to say instead. Every field is normalised before any consumer touches it.
OutputPer-person cockpitA Google Sheet per rep and per team, re-rendered daily: scores, trends, weekly review, a manager summary every Monday. Each renderer owns its whole spreadsheet.
OutputFlag emails and briefsA red-flag email to the owner when a call goes wrong. A personalised Google Doc brief to each expert about 30 minutes before their call, built from the lead's application and history.
OutputLead scoreEach expert call also produces a score for the lead. Tier A leads went on to buy at 12.4%, tier C at 0%, on 784 scored calls. The score is written back to the CRM and drives follow-up.
ReuseMock-call assessmentsThe same engine scores recorded mock calls for sales hiring, so candidates are judged on the rubric the team is coached on.

03 — How it worksDesign decisions that keep the scores honest.

  1. The scorer is never told the outcome. A score that knows the deal closed is circular. Outcome joins happen after scoring, in the report.
  2. Approved claims are never flags. Accreditation, financing eligibility, outcome statistics and dual-currency pricing are true things reps are allowed to say. The rubric carries the allow-list so the model does not penalise them.
  3. Empty or short-for-duration transcripts are not scoreable. A 40-minute call with 200 words of transcript is a recording failure, not a bad call, and is marked as such.
  4. Durable writes before cosmetic renders. The score upsert runs first; the sheet render runs after. A render bug can no longer take the week's scores down with it.
  5. Budgeted runs. Each run scores a capped batch and resumes from a watermark, because an edge function has 150 seconds and a backlog does not.

04 — What brokeThe failures that shaped the rules above.

A render killed the weekOne LLM response returned a non-array where an array was expected. The weekly render crashed before the database write, so no rep saw a review for ten days while the scheduler reported success.
Normalise, then write, then renderEvery LLM field is coerced to its expected type. The upsert moved ahead of the render. A date override was added so a missed week can be re-run.
Inline transcript queriesFetching transcripts per call inside the run timed out and repeated rows.
Sweep into a cacheMetadata is swept in windows of up to eight hours with hard page timeouts, cached, and deduplicated. Scoring reads the cache.
Closest-in-time matchingA transcript was matched to the wrong lead because it was the nearest call on the calendar.
Positive identification onlyMatching needs the lead's email, a title phrase, or the expert's name. Otherwise the call is left unmatched and listed for a human.
Hand-edited mirrorsFixes applied directly to a cockpit the team also maintained were overwritten by the next CI deploy.
One source of truth per cockpitEach cockpit has an owning repo and ships through CI. The monorepo copy is never hand-edited.

05 — What I'd do differentlyValidate the rubric against outcomes before scaling it.

The lead score earned its place: the tier gradient against wins is real. The behavioural scores for reps have not yet shown a measured link to conversion, and expert quality was flat between March and July on the rubric. I would have run the outcome validation on a few hundred calls before building three cockpits on top, and I would hold the coaching claims to the same standard I hold the lead score.