Inspiration
Tradespeople, clinics and small shops lose work every day to missed calls. Callers rarely leave a voicemail, and when they do, nobody listens to a two-minute recording between jobs. The answering machine was a good idea killed by its format: you had to rewind. Afterbeep keeps the machine and drops the tape.
What it does
When nobody answers, Afterbeep records the caller, transcribes the message (English or German) and writes a card: who called, the callback number, why, how urgent, and a suggested reply. The card is emailed within seconds and lands on a tape-deck dashboard where every call is a cassette with a colored urgency edge. Mark a call handled and it moves to the shelf.
A callback number is only accepted if its digits appear in what the caller said or in the caller ID, so a card can never invent a number you would dial. Audio is deleted once the card exists, and cards are hidden after 30 days (automatic deletion of the rows is next).
Try it: open the demo, click "Try the demo", press "Record a call", speak for ten seconds and watch your own card appear, with no login. The demo records in the browser; the real phone line (sipgate or Twilio) is the next step and is not built yet. In the public demo, card emails only go to allowlisted addresses.
How we built it
Next.js 16 and TypeScript (strict) on Vercel, Supabase Postgres with row-level security and no public policies, Groq Whisper for transcription, Claude for extraction against a zod schema, Resend for mail. The core is runPipeline, a pure orchestration function with storage, audio, mail and the clock injected as ports, so the whole pipeline runs in tests without a network. Browser calls are recorded as 16 kHz mono WAV with an AudioWorklet and a hand-written encoder, so the silence check and the transcription see the same audio.
Challenges
- Trustworthy extraction. A hallucinated phone number is worse than no card. The guard checks digit sequences against the transcript and caller ID, retries once, and otherwise returns a labelled fallback card with the transcript kept.
- Privacy by construction. Audio is deleted after processing, cards expire, and the demo workspace is a signed cookie with seeded data only.
- Browser audio. Recording WAV on the client avoided codec mismatches between the browser, the silence detector and the transcription API.
- Real deployment bugs. The live site broke on a wrong Supabase query and on audio uploads sent with the wrong content type. Both were found by running the real pipeline end to end and fixed by hand.
Accomplishments
- 126 tests across 11 files, including property tests (fast-check) that the pipeline always ends in a terminal status and that WAV header lengths are always consistent.
- A dashboard that is the product's idea, not a template: seven-segment message counter, reels that turn only while a call is open or recording, urgency as a physical edge on the cassette.
- No code comments in the codebase, held by the
code.mdsteering rule across all Kiro runs (a save hook for it is in the repo but was not run).
What we learned
Specs with EARS requirements made Kiro's output far more consistent than free prompting, and steering files are where house rules actually stick. Headless Kiro without a shell is good at writing and bad at verifying, so every batch still needed a type check and a test run outside Kiro.
What's next
A real phone number per customer (sipgate or Twilio), calendar links in the suggested reply, per-owner login, and a plan at 9 EUR per number per month.
How we used Kiro
I want to be exact about what Kiro did and what I did by hand. The /.kiro folder in the repo is the real record.
Which Kiro: the Kiro CLI, run headless in a cloud workspace and restricted to reading and writing files. I did not use the Kiro IDE, Kiro Web or Kiro Crew.
Steering (.kiro/steering: product, tech, style, code, privacy). Kiro wrote these in the first run from my brief, and every later run used them. The strategy that mattered most was one blunt rule file: code.md says code has no comments, and it held across all runs. style.md was rewritten by Kiro to the design brief (palette, type, motion rules), which kept the dashboard consistent.
Spec-driven development (.kiro/specs): three specs with EARS requirements and designs, two of them with task lists: call-pipeline (22 tasks) and demo-session (25 tasks); dashboard has requirements and design only. Kiro wrote the specs and then implemented the pipeline library, ingest route, email renderer, repositories, dashboard pages, demo session, recorder and WAV encoder from them. Compared with free prompting, the specs gave me checkable tasks and stopped the model drifting between runs.
Property-based testing: fast-check tests were written by Kiro for the pipeline (always reaches a terminal status) and the WAV encoder (header lengths).
Agent hooks (.kiro/hooks): three hooks: house rules on save (no comments, no long dashes, no any), an extraction regression check, and a spec sync check. I wrote these three files by hand and never ran them in the Kiro IDE, since the build ran through the CLI. They are in the repo as a working design, but I cannot claim they improved this build.
MCP (.kiro/settings/mcp.json): Supabase (read only) and Vercel. Written by hand, and not used by Kiro during the build.
Custom agent (.kiro/agents/call-pipeline.json): restricted to the call pipeline paths. Written by hand, and not used for the main build.
Kiro powers: none used.
Where Kiro fell short, and what I fixed by hand:
- A Blob typing error in the transcription client and again in the recorder (copy the bytes into a fresh Uint8Array).
- A contradictory sort rule in the call list helper, removed.
- The ingest route failed open when its secret was unset, changed to fail closed.
- The in-memory repository lost state across Next bundles, moved to
globalThis. - The recorder only showed in the empty state, moved to the top of the dashboard.
- Added the Whisper model field and updated the default extraction model.
- Fixed the Supabase list and detail queries, and the audio upload content type.
- Added the demo mail allowlist, session reuse and request limits, which the generated routes lacked.
- Kiro was throttled several times by the service. I retried after waiting.
Git history: 24 commits, all authored as cornex5. Commit messages do not separate generated from hand-fixed code; the hand fixes are listed above.
Best Startup Project
I am the founder: Jonas Klein. Startup website: https://afterbeep-demo.vercel.app, LinkedIn page: https://www.linkedin.com/company/afterbeep-app
Built With
- claude
- groq
- kiro
- nextjs
- resend
- supabase
- typescript
- vercel
