A parked aviation cold-calling script became Deals Machine after winning two tracks at Milan AI Week's hackathon, and now runs live sales calls for 30 pilot teams
Speechmatics' real-time API transcribes each call live so an agent can drop coaching cards in under two seconds, while the batch API reprocesses the recording afterward with diarization for a post-call scorecard
Four bugs stood between a hackathon demo and a trustworthy product: gating audio on the WebSocket open event instead of Speechmatics' own RecognitionStarted ack, reusing real-time config on the batch endpoint, discarding Speechmatics' typed error responses, and a hardcoded language config that silently mistranscribed non-English calls
The throughline: Speechmatics' real-time API is a stateful protocol with its own handshake and config rules, not a drop-in swap for the batch API
Speechmatics' own outbound team is now using Deals Machine to run its own sales calls, and their feedback is shaping the product roadmap
I didn't set out to build a company. I entered a hackathon because I wanted a free ticket to Milan AI Week.
In February 2026 I started working for a guy who'd spent 20 years as a cargo charter broker, booking aircraft to move freight. He put me straight on the phones: a minimum of 20 cold calls a day, no training, none of the industry's jargon. Around the same time the Strait of Hormuz closed, sea freight couldn't get through, and cargo shifted to air, exactly where we made our money. The planes were booked solid, but we still weren't getting leads.
So I built a rough script for myself. Every morning an agent scanned the news, worked out which industries were under pressure, and told me who to call and what angle to open with. I recorded every call and fed the outcome back in, and it sharpened the opening angle for the next one. I closed a deal right at the end, then moved on from the role and parked the tool.
A few months later I saw the AI Agent Olympics Hackathon, the official builder event of Milan AI Week 2026, run by lablab.ai. The brief was to build an enterprise-level tool using multi-agent collaboration, almost exactly what I'd already built and parked. I pulled it back out and rebuilt it, turning it from an aviation-specific hack into something any sales team could use by describing their vertical in a paragraph. Plugging in Speechmatics added the piece that had been missing: live coaching during the call, not just better targeting before it and analysis after. I placed first in both the Vultr track and the Speechmatics track, out of roughly 2,000 builders.
What it does: a human-driven cold-call cockpit, not an autopilot. You describe your vertical, an agent pipeline researches the market and writes a playbook, and when you call, it rings your own phone. You talk to the prospect. Speechmatics transcribes the call live, and an agent reads the rolling transcript and drops coaching cards into the cockpit in under two seconds. The AI never speaks to the prospect. The human does.
After the hackathon I posted a simple video documenting the journey. Not launching anything, just telling the story. The reaction caught me off guard. People started applying before there was even a way to sign up. Within a week I'd had over 100 applications for what I'd planned as a 25-person private pilot. I doubled it to 50 spots and spent two straight weeks doing back-to-back demo calls just to keep up.
Deals Machine now has 30 teams in private beta: real estate operators, digital agencies, freight and logistics brokers, growth and outbound startups. Some bring their own lead lists and just want the coaching layer on top; others use it end to end. It's a 14-day free trial, and I've run almost every onboarding call myself.
Going from something that worked once on a hackathon stage to something 30 teams could put real prospects through meant the live coaching pipeline had to survive contact with reality. Four bugs in particular marked the real distance between a rehearsed demo and a product people could trust on live sales calls, and all four came down to the same root cause: treating Speechmatics' real-time API as a full protocol with its own rules, not just a socket you throw audio at.
The first real dial test produced an empty coach panel. No cards, no errors. Worker logs showed audio flowing and Speechmatics acking the connection, which pointed everywhere except the actual problem.
The bug was a race condition in the code that's supposed to know when Speechmatics is ready:
A WebSocket's open event only confirms the transport connected. Speechmatics still needs to receive StartRecognition and reply with its own RecognitionStarted acknowledgment before it's actually processing audio. On a short call, that 100 to 500ms gap was long enough for the first several audio frames, often the entire opening line, to get sent and silently dropped, because sendAudio() was gated on the wrong flag.
The fix: make open() a promise that only resolves once RecognitionStarted actually arrives, and gate all outgoing audio on that, not on the socket state.
The lesson generalizes: when an API has its own application-level handshake on top of a WebSocket, gate real work on that handshake's ack message, not on the transport connecting. A socket being open and a service being ready are two different facts, and the gap between them is exactly where a live product loses its first few words.
We call Speechmatics two ways: the real-time WebSocket API for live coaching, and the batch REST API to reprocess a recorded call afterward for a post-call scorecard. We copied the real-time config into the batch config, including enable_partials, and Speechmatics' batch /jobs/ endpoint rejected it outright with an HTTP 400 (enable_partials is not allowed). That field only exists on the real-time side; batch doesn't have an equivalent concept. One-line fix, but worth flagging for anyone integrating both surfaces: don't assume config is portable between them without checking each schema.
For a while, any Speechmatics failure surfaced in our app as “No transcription provider configured,” even when the API key was set correctly. That one stung because Speechmatics' real-time API doesn't actually fail silently. Every failure comes back over the WebSocket as a structured Error message: a type field (not_authorised, invalid_config, invalid_model, quota_exceeded, job_error, and a handful of others), a human-readable reason string, and a numeric code, before the server closes the connection. That's specific, actionable information on every single failure.
Our dispatcher had a secondary error-handling path meant to add context if something else went wrong, and it threw all of that away. The moment the real Speechmatics error wasn't one of the two or three cases we'd explicitly coded for, type, reason, and code got discarded and replaced with that one generic fallback string. The fix was simple: hold onto the actual type and reason Speechmatics sent, and surface those instead of defaulting to a catch-all. When an API hands you a typed, specific error on every failure, it's telling you something. Don't build an error path that throws it away.
The three bugs above got the coaching loop stable enough to trust. The next test was going multilingual, once pilot teams started running calls in French and Spanish. That surfaced a bug that had been sitting in the code since the aviation-only prototype: the real-time transcription config had language: 'en' hardcoded.
This one didn't throw an error either. A French call still connected, still streamed audio, still got transcribed, just as English-shaped noise. The coaching agent read that transcript and confidently generated cards off nonsense. Meanwhile the batch reprocessing path, built later, already used language: 'auto' and had no such problem, which is exactly why nobody caught the mismatch until a real non-English call went live.
The fix was straightforward once found: thread the org's configured locale into the real-time session instead of a hardcoded literal.
Worth flagging for anyone starting fresh today: Speechmatics has since deprecated operating_point in favor of a model field, with standard, enhanced, and melia-1 as the current values. Same idea, new name.
The second bug was a judgment call, not a code defect. Speechmatics had just announced Melia, a multilingual model with automatic code-switching across 55+ languages. The obvious-looking fix was “just point everything at Melia and stop caring about language per call.” That's the right move for the batch leg, where a rep's post-call recording can be auto-detected and reprocessed after the fact. It's the wrong move for the live leg: Melia is currently batch-only, in production preview, with real-time support on the way but not yet shipped. Real-time transcription today still runs on the Standard/Enhanced models, which need to be told the call's language up front. For the coaching cockpit, “multi-language” meant reading the language we already knew from onboarding and passing it explicitly, not swapping in a model that isn't available on that surface yet.
Same root cause as the first three bugs: Speechmatics isn't one API, it's a real-time protocol and a batch API that happen to share a brand name, with different config schemas and, now, different model availability. Assuming symmetry between them is where all four bugs came from.
The live leg is the part people see, but it isn't the only place Speechmatics does work in this pipeline. Every call also gets reprocessed after it ends, through the batch API, with the enhanced model and diarization turned on: full speaker separation, not just a transcript. That diarized transcript feeds a scorecard: what worked in the call, where the rep lost the prospect, what to change next time, plus a growing set of extracted facts about that vertical that gets reused the next time someone on the team picks up the phone.
The live pass gets a rep through the call in real time. The diarized batch pass is what makes the system get smarter about that vertical afterward. Same API, two different jobs on two different surfaces, which is exactly why bugs 2 and 4 existed in the first place, and exactly why we need both rather than picking one.
All four of these are variations on the same lesson: Speechmatics' real-time API is a stateful protocol with its own handshake, its own config rules, and its own set of available models. It isn't a drop-in replacement for whatever you'd build against the batch API. Respecting that (waiting for the real “ready” signal, reading the typed error instead of a generic fallback, not mixing up real-time and batch config, and not assuming a batch-only model works on the live path) is what took the coaching loop from working once on a hackathon stage to holding up across real calls with 30 pilot teams, in languages and industries I never designed it for, on phones I've never touched.
It also paid off architecturally. The real-time session class (mint a JWT, open the WebSocket, send StartRecognition, wait for RecognitionStarted, stream raw audio, emit partial and final transcripts) ended up provider-neutral by design: it just eats audio bytes and hands back a transcript stream. That seam is what let us plug the same Speechmatics integration into calls placed over Twilio and calls coming in over browser/WebRTC, without touching the Speechmatics layer at all.
Somewhere in the middle of fixing all of this, Speechmatics' own outbound team started running real sales calls through Deals Machine: the team behind the API this entire product is built on, using it to book their own meetings. That's not a logo I get to claim lightly. It's the people who built the transcription layer underneath us, pointing their live pipeline at what we built on top of it.
Their feedback shaped the actual roadmap, not a testimonial page: teach the system from a team's own call history instead of generic playbooks, connect coaching directly to deal outcomes instead of stopping at the call, extend language coverage further, do CRM integration properly instead of as an afterthought, and design it to sit on top of the stack a sales team already runs instead of asking them to rip it out. Those five things are load-bearing parts of the roadmap now, because the team closest to the API told us, from their own seat as a user, what a live sales tool has to get right to be trusted.
I'm heading to the RAISE Summit in Paris this month, part of the hackathon prize, on Vultr's stage, in front of a room full of investors. Right after that, it's back to onboarding the beta cohort and working toward turning this from a solo hackathon build into a funded company with a real team. The number I'm holding myself to: 500 active sales reps using it by the end of the year.
It's a strange thing to say out loud, but the honest timeline is this: entered a hackathon to get a free ticket in May, won two tracks, posted a video about it, and ended up with 30 companies running real sales calls through a live coaching loop that didn't exist three months ago.

The move brings speech recognition directly into CATalyst VP eliminating the challenges of running multiple applications, making it the first seamless solution for the voice reporting industry.
![[alt: Speaker lock blog image]](/_next/image?url=https%3A%2F%2Fimages.ctfassets.net%2Fyze1aysi0225%2Fue7vVoLyWYL8hohG7JNz6%2F0df9783d84d0e93b5b04f8ecbe33d8f0%2FSpeaker_lock-blog-v1_-_Header_16-9.webp&w=3840&q=75)
Because in the real world, conversations are messy, and Voice AI needs to keep up.

How Wellcom Health uses real-time transcription, medical-grade accuracy and diarization to turn messy Dutch consultations into validated clinical reports.

Learn how to build Voice AI applications with real-time transcription, including APIs, architecture, latency, scalability, and deployment best practices.

Quantization was the key to fitting a cloud-grade model on a laptop. Getting the full optimization chain to cooperate around it was the hard part.
