Inspiration

Hiring is broken at the first-round stage.

Recruiters spend an average of 23 minutes scheduling, attending, and documenting a single phone screen, and most of those calls are disqualifying within the first five minutes. Multiply that across $50$ candidates per role, across dozens of open positions, and the maths becomes painful:

$$ \text{Time lost} = \text{candidates} \times \text{avg. screen duration} $$

$$ = 50 \times 23\text{ min} $$

$$ = 1,150\text{ min} \approx 19\text{ hrs per role} $$

We'd already built HireView and seen this problem up close: the scheduling back-and-forth, the timezone juggling, the no-shows. But most async video tools are either enterprise-locked (think HireVue at $25k+/year) or clunky consumer tools repurposed for hiring.

TakeOne was born from one question: what if the entire first-round interview was just a link?


What It Does

TakeOne is an async video screening platform built for recruiters and hiring teams.

  1. Recruiter creates interview (tailored/pre-set) and select interviews questions, tag requirements
  2. Candidate receives a shareable link, no account needed
  3. Candidate records video answers directly in the browser
  4. Recruiter reviews answers in a dashboard, reads AI-generated transcripts and summaries, rates candidates, and moves them through a pipeline

The result: a first-round interview that takes candidates 10 minutes to complete and recruiters 8 minutes to review, on their own schedule, with no calendar coordination required.


How We Built It

Stack

Layer Technology
Frontend Next.js 14 (App Router)
Deployment Vercel
Primary Database Amazon DynamoDB
File Storage Amazon S3
Transcription AWS Transcribe
Auth NextAuth.js
AI Summaries AWS Bedrock (Claude)

Why We Moved Off Our Original Stack

TakeOne's first version, built as HireView, ran on Laravel + MySQL, with OpenAI Whisper for transcription, GPT-4o-mini for summaries, and Lambda + SQS workers for async processing. Rebuilding it on the hackathon's required AWS stack meant real tradeoffs, not just a vendor swap:

Scalability

  • Technical: DynamoDB scales horizontally with zero ops, but our synchronous, queue-free pipeline (one API route per candidate submission, sequential bulk send) is the real ceiling. It won't reach "million-candidate" scale without bringing Lambda + SQS back; that's a deliberate hackathon-scope tradeoff, not an oversight.
  • Financial: DynamoDB on-demand has near-zero idle cost: no flat instance bill like RDS. But Transcribe and Bedrock charge per minute/token, so at real volume, AI cost, not database cost, becomes the dominant line item, a pricing question for the product as much as the infra.

Reliability

  • Technical: Dropping Lambda/SQS removed a moving part, but it also removed the automatic retry/backoff a queue gives for free. We hand-built a manual safety net instead: a reprocess endpoint that re-transcribes from the stored S3 video when submit-time AI fails.
  • Financial: Fewer infrastructure pieces means lower AWS-credit burn and simpler cost monitoring during the hackathon. But a failed 180-second synchronous request still burns Vercel execution time and Bedrock/Transcribe charges before it can be retried. There's no free retry the way a dead-letter queue gives you.

Functionality

  • Technical: Keeping transcription and AI scoring inside AWS means candidate data never leaves the AWS account boundary: one IAM model end to end, which matters for an HR product handling sensitive candidate data.
  • Financial: That benefit isn't free: AWS Transcribe runs roughly 4x OpenAI Whisper's per-minute price, and Bedrock Claude Haiku runs roughly 7–8x GPT-4o-mini's per-token price. We're paying a real, quantifiable premium for data locality and a single vendor relationship: a tradeoff we'd make again, but worth naming honestly.

Was it worth it?

  • At hackathon scale, the migration didn't strictly have to pay for itself: a synchronous app on a relational database would have run fine too, and the AI cost premium is small in absolute dollars at low volume.
  • The real case isn't about today's traffic, it's about which problems are cheap to fix later and which aren't:
    • Swapping an AI vendor, or adding a queue in front of an already-synchronous handler, is a contained, afternoon-sized change.
    • Re-architecting a relational schema once a product is live and under load, read replicas, connection pooling, sharding, is not.
  • We paid the AI cost premium now, on the part of the system that's genuinely easy to revisit later, in exchange for a data layer that doesn't need to be rebuilt as volume grows.
  • The tipping point: once concurrent interview volume is large enough to strain a single relational connection pool and a handful of read replicas, a relational rewrite gets expensive, and a DynamoDB-based system just keeps running, unchanged.
  • Below that scale, this is a bet on not having to do the migration twice. Above it, it's the only part of this stack that doesn't need a second rewrite.

Database Design: DynamoDB

We chose DynamoDB as our primary database and designed it as a single table with no GSIs. Everything — interviews, questions, answers, clients, positions, requirements, candidates, tokens — lives in one table keyed by pk and sk.

The schema is driven entirely by the four access patterns that cover every hot-path read in the product:

# Use case Key condition
1 List all interviews pk = IV, sk begins_with IV#
2 Resolve a candidate token pk = TOKEN, sk = {token} (single GetItem)
3 Fetch one interview + questions + answers pk = IV, sk begins_with IV#{id}
4 List positions under a client pk = CLIENT#{id}, sk begins_with POS#

Every entity maps to a row in that table:

Client            pk=ORG              sk=CLIENT#{id}
Position          pk=CLIENT#{id}      sk=POS#{positionId}
Requirement       pk=POS#{id}         sk=REQ#{sort}
Interview         pk=IV               sk=IV#{id}
Interview question pk=IV              sk=IV#{id}#Q#{sort}
Answer            pk=IV               sk=IV#{id}#ANS#{questionId}
Token lookup      pk=TOKEN            sk={uniqueToken}
Candidate         pk=CAND             sk=EMAIL#{email}

Grouping an interview with its questions and answers under the same partition key (IV) means fetching everything a recruiter needs to review one candidate is a single Query, not a join. Token resolution is a GetItem. No secondary indexes needed, no fan-out on write, no consistency lag.

The deliberate constraint: we gave up ad-hoc querying. Any future access pattern that doesn't fit one of the four above needs to be designed in explicitly. That's the real cost of single-table design, and it's the reason the schema had to come before the code, not after.

Video Pipeline

Browser (MediaRecorder API)
        ↓
  S3 Presigned URL upload
        ↓
  S3 Event Notification
        ↓
  AWS Lambda trigger
        ↓
  AWS Transcribe job
        ↓
  DynamoDB update (transcript + status)
        ↓
  AWS Bedrock → AI summary written back to DynamoDB

The video never touches our Next.js server: candidates upload directly to S3 via a presigned URL, keeping latency low and costs down.

Frontend

The recruiter dashboard was built with Next.js App Router and deployed on Vercel. We used Vercel's native environment variable integration to securely store AWS credentials and connect the frontend to DynamoDB via the AWS SDK v3.

The candidate recording interface uses the browser's native MediaRecorder API, no third-party video SDK required, with a countdown timer, question display, and re-record option.


Challenges We Faced

1. Cold-start latency on AWS Transcribe

AWS Transcribe jobs have a non-trivial startup time (~10–15 seconds). We solved this by making the transcript an async update: the recruiter dashboard shows a "transcribing…" state and polls via a lightweight API route until the job completes.

2. DynamoDB single-table vs multi-table design

We initially explored a single-table design to keep costs minimal, but the access patterns across recruiters, jobs, and candidates were sufficiently different that separate tables with a GSI made the query logic far cleaner and more maintainable.

3. Browser video compatibility

MediaRecorder codec support varies across browsers. We had to implement a codec negotiation fallback:

const mimeType = [
  'video/webm;codecs=vp9',
  'video/webm;codecs=vp8',
  'video/webm',
  'video/mp4',
].find(type => MediaRecorder.isTypeSupported(type));

4. Candidate UX with no account

Letting candidates submit without creating an account introduced a statefulness challenge: what if they close the tab mid-recording? We solved this with a short-lived session token embedded in the invite link, stored in localStorage, that lets candidates resume an incomplete submission within 24 hours.


What We Learned

  • DynamoDB access pattern design must come first. The schema follows the queries, not the other way around. Getting this right early saved significant refactoring later.
  • Vercel + AWS is a genuinely powerful pairing. Edge functions on Vercel talking to DynamoDB via the AWS SDK v3 is fast, cheap, and deployable in minutes.
  • Async doesn't mean low-touch. The candidate experience still needs to feel warm and guided: a bare recording interface creates anxiety. Small UX details (question preview, time limit display, re-record option) dramatically improved completion rates in testing.

What's Next

TakeOne v0.3 covers the screening and video interview layer. The roadmap extends the platform into a full hiring pipeline: from job posting to offer.

Extending left: job posting and applications The next milestone is a public job board page per position, candidate self-apply with CV upload to S3, AI-powered CV parsing straight into the candidate directory, and an automatic screening invite triggered on application. Recruiters go from "role open" to "candidates screened" without touching a second tool.

Extending right scheduling and team collaboration After screening, candidates need to book a live interview. We'll add recruiter availability slots, candidate self-book links, and Google/Outlook sync — eliminating the calendar back-and-forth that async screening was designed to avoid in the first place. Hiring manager share links and multi-recruiter scorecards follow, moving TakeOne from a solo tool to a team product.

ATS and HRIS integrations becoming the screening layer inside other platforms The most significant opportunity is positioning TakeOne as the video screening layer that sits inside existing ATS platforms (Greenhouse, Lever, BambooHR) and HRIS systems (HiBob, Personio). Rather than asking recruiters to switch tools, TakeOne accepts inbound webhooks when a candidate reaches a screening stage, automatically creates and sends the interview from a mapped preset template, and writes a structured AI scorecard back to the ATS candidate record, overall score, per-requirement assessments with evidence, recommendation, and a direct video playback link. The ATS gets AI-powered screening without building it; TakeOne gets distribution across every ATS customer without a sales team.

Structured scorecard as a compliance asset Every TakeOne score is traceable to a transcript, every transcript to a question, every question to a documented requirement. This audit trail model version, timestamp, evidence sentence per score, gives HR and legal teams a defensible, bias-documented screening process. We plan to expose this as a downloadable compliance report per candidate, which is the feature that turns ATS partnerships from a nice-to-have into a procurement requirement.

Offer management and usage-based billing The pipeline closes with offer letter generation, e-signature integration, and outcome tracking. Pricing will be usage-based, charged per active job posting rather than per seat, keeping TakeOne accessible to early-stage startups while scaling naturally with larger teams.


Built With

Share this project:

Updates