Studio
Brand & Content
Creative strategy, content and production that bring your brand to life and make it unforgettable.
- Branding
- Commercial Execution
- Video Production
- Content Production
- Creative Campaigns
- Motion Design
- AI Content Production
Popular pages
Common sections
Ask with AI Beta
Ask anything about our services, process, pricing or how we can help. The guide answers, asks what it needs to know, and recommends — without leaving the page.
Example prompts
Case study · Metmee
Metmee runs first-round interviews without a calendar. A recruiter describes the role and shares one link; candidates record their answers when it suits them. Every answer is transcribed in any of around 40 languages, proofread, and scored by Claude against the recruiter’s own weights — with the reasoning written out, and the decision left to a person.
Answer 2 of 401:48
Last year we moved a monolith to microservices. We deployed on Kubernetes, ran CI/CD through GitHub Actions, and watched it all in Grafana and Prometheus. The hardest part was splitting the database without any downtime.
77overall · weighted 40 / 30 / 30
Counted from the Metmee repository at commit 7fb6448, 10 September 2026.
The first requirements document asked for eleven things: a recruiter writes a job description, the AI suggests questions, candidates record video answers through a link, and the AI writes a qualitative assessment. What shipped is that product — rebuilt at each point where the MVP would have failed a real candidate, on a real connection, in a real language.
| Area | Specified · 31 May 2026 | Shipped · 10 September 2026 |
|---|---|---|
| Assessment | Qualitative prose, without numeric scores. | Scores for technical depth, communication and culture fit, per question and overall — the overall computed from the recruiter’s weights, not by the model. |
| Transcription | Responses are transcribed and sent for assessment. | Transcribed on the server for every browser: three providers in a chain, a vocabulary curated per interview, and a proofreading pass that is not allowed to invent. |
| Recording | Camera and microphone through the MediaRecorder API. | Camera, or audio only when the camera fails; five recording formats tried in order; the bitrate capped so a phone can upload what it records. |
| Access | Row-level security: each recruiter sees only their own interviews. | The same, plus a secret per candidate session on every write, rate limits by route, and approval before a new recruiter account can use the product. |
| Timing | A 30-second timeout on AI requests. | A budget per job: 20 s for a follow-up, 30 s for question suggestions, 60 s per clip, 90 s for an assessment, 300 s for everything after a candidate submits. |
| Questions | A fixed set, suggested by AI and edited by the recruiter. | The same — or an adaptive interview, which can ask up to three follow-ups per question, written from what the candidate just said. |
| Language | Not specified. | Interviews, transcription and assessment in around 40 languages. |
The first station is the one with the least control: somebody else’s phone, somebody else’s connection, a browser that says it supports a format it does not. So nothing here assumes. Each choice is a ladder, tried from the top, with a floor that still lets the interview finish.
Camera and microphone
Floor Audio only The interview still completes. A recruiter can also set an interview to audio from the start.
Recording format
Floor The browser’s own default Used outright on OEM Android browsers, which report support they do not have.
MB per minute of answer
Upload, bytes sent over time
Safari on iOS records at 4–8 Mbps by default — 30 to 60 MB for every minute of talking head, which then fails to upload on a phone. 1.2 Mbps is plenty for 720p.
Each clip goes from the browser to a private bucket on a signed URL. It never passes through the application server.
An upload is abandoned only when no bytes have moved for 45 seconds. A slow connection that keeps moving is allowed to finish.
Signed upload tokens are single-use, so each of the three retries asks for a new one before it starts.
Leaving the tab is logged against the question. The browser’s own camera dialog is not — and neither is the second and a half after it closes.
Five minutes by default, set per question, with an optional limit on the interview as a whole.
A candidate for a technical role says “we deployed on Kubernetes and watched it in Grafana” in one breath — and when they speak another language, the English terms arrive in the middle of it. A general speech model mangles exactly those words. Metmee transcribes in two passes: a speech model that has been told which words to listen for, then a proofreader that may only fix what was misheard.
Collect
Words from the job title, the description and the questions
Curate
Claude Haiku keeps the 15–40 a recogniser would miss. Once per interview, shared by every clip
Hear
Speech-to-text, routed by language and length. Three providers, tried in order
Proofread
Claude, temperature 0. Spelling, jargon and punctuation only
Check
Length must stay between 0.5× and 1.6× of what was heard. Otherwise the raw transcript stands
| Answer | Model | How it listens |
|---|---|---|
| English | AssemblyAI Universal-3.5 Pro | A short prompt naming the role and the question, plus the curated terms. |
| Other languages — under 20 s | AssemblyAI universal-2 | Fixed to the interview’s language: detection misfires on clips this short. |
| Other languages — 20 s and longer | AssemblyAI universal-2 | Language detection with code-switching on — so English terms inside another language are heard as English. |
If a provider fails, or hears nothing
const rawLen = rawText.trim().length;
const corrLen = corrected.length;
if (rawLen > 0 && (corrLen > rawLen * 1.6 || corrLen < rawLen * 0.5)) {
console.warn(
`[transcription] correction discarded (length drift raw=${rawLen} corrected=${corrLen}); keeping raw`
);
return null;
}
Claude returns a score from 0 to 100 for technical depth, communication and culture fit, for each question and overall, with the reasoning written out. The overall that ranks one candidate above another is not the model’s number: it is the recruiter’s weights, applied the same way every time — which is what makes a ranking something a person can explain.
Ranking · illustrative candidates
Each candidate’s three scores stay fixed — as the model returned them. Only the weights move.
// Deterministic weighted overall — keeps ranking explainable & stable.
const { technical, communication, culture_fit } = input.weights;
const totalWeight = technical + communication + culture_fit || 1;
const overallScore = Math.round(
(technicalScore * technical +
communicationScore * communication +
cultureFitScore * culture_fit) /
totalWeight
);
Strictness is written into the prompt as explicit score ranges, so “strict” means the same thing on every assessment. The same generic answer is worth 60–70 at the first level and 20–40 at the fifth.
| Level | Generic answer | Weak or empty | On a scale of 0 to 100 |
|---|---|---|---|
| 1 Lenient — entry-level roles | 60–70 | 30–45 | |
| 2 Moderately lenient | 55–65 | 25–40 | |
| 3 Balanced — the default | 45–60 | 15–35 | |
| 4 Strict | 35–50 | 5–20 | |
| 5 Very strict — senior roles | 20–40 | 0–15 |
The prompt tells the model the transcript is machine-made: filler words, repetitions and recognition errors are not held against the candidate.
Contradictions between answers, claims that do not hold together, answers that sound memorised or evasive — and an empty field when there are none. The prompt says, in capitals, not to invent them; the interface calls them “points to verify”.
When a long interview runs the model into its token limit, the truncated JSON is repaired — the open string closed, the dangling comma dropped, the brackets balanced — instead of failing with a 500.
A rate-limited call waits 2, 4, then 8 seconds and tries again. Anything else fails, marks the assessment failed, and puts a retry button in front of the recruiter.
In an adaptive interview, each answer is read between one question and the next, and the model decides whether a single follow-up would tell the recruiter something the answer did not. The right answer is often no, and the prompt spends as many lines on when not to ask as on when to.
About 25 words at most, built on what the candidate actually said.
Set per interview, between none and three; one by default.
Under eight characters of transcript, the model is not called at all.
The follow-up is stored against that session alone, so no one else is ever asked it.
Everything upstream arrives as one assessment, in a shape the model is not free to change: the prompt names every field it must return, and the product checks them on the way in. The overall is not among them — the product adds it. The decision is the one thing on the page the software does not make.
What the model must return — and what the product adds
strengths stringweaknesses stringrecommendation stringrecommendation_level strong_yes | yes | maybe | nored_flags stringtechnical_score 0–100communication_score 0–100culture_fit_score 0–100per_question [{ index, score, comment }]overall_score 0–100Pace, words per minute
Speech, measured without a model
| Level | Shown as | Derived when |
|---|---|---|
strong_yes | Strongly recommend | 80 and over |
yes | Recommend | 65 to 79 |
maybe | Consider | 45 to 64 |
no | Do not recommend | Under 45 |
The four levels the model may return, and what the interface calls them. The thresholds apply only when the level has to be derived from the overall.
Every speech metric is a count or a ratio from the transcript: free, instant, reproducible — and it never touches the score.
Accepting or rejecting a candidate records the decision and tells the candidate by email.
Answers play from a private bucket on a signed URL that expires after an hour.
Every candidate for a role on one table, and any single assessment as an A4 PDF.
A recruiter signs in, and row-level security keeps them inside their own interviews — on all eight tables. A candidate does not sign in at all, so every candidate write passes its own gates, in this order, before anything reaches the database. Responses carry HSTS, a policy that forbids framing, and a connect list that lets the browser talk to the app, Supabase and Anthropic, and nothing else.
| Scope | Window | Requests |
|---|---|---|
| Authentication | 60 s | 10 |
| New candidate sessions | 60 s | 20 |
| AI question suggestions | 60 s | 15 |
| Everything else | 60 s | 60 |
Candidate routes run with the service role, and at first they trusted the session ID alone: anyone who had a session’s ID could write to it. A secret issued with each session, and required on every candidate write, closed it. Sessions created before the change hold no secret — and a missing secret never matches, so nobody can write to them.
The rate limiter keeps its counts in memory, per server instance, in fixed one-minute windows. It stops bursts and brute force; it is not a global limit across instances. The project’s README says so, and names the fix: back it with Redis.
The core and the production pass landed in the first week of June. The second wave, in July, added what the first month of use asked for. The third, in September, went back to the hardest station and rebuilt it.
Every number on this page, and the rest of them, from one reading of the repository.
Services on this project
Next project
ArtCraftTell us what you want to change. We’ll discuss the work that would support it.
Discuss your projectRecommended for you
Combine technology with creative execution to unlock even bigger results — from strategy to scale, all in one place.
Brand & Content
Creative strategy, content and production that bring your brand to life and make it unforgettable.
Digital / Explore / What’s nextDifferent expertise. A stronger tomorrow.