by Lab Rats

Developer API v1

Create interviews, send recipient links, and collect voice answers + transcripts from your own app. Generate a key in Dashboard → Developer. Each recipient link uses 1 credit (or is billed Pay As You Go). Rate limit: 60 requests/min per account.

Authentication

Authorization: Bearer ea_live_xxxxxxxx

Create an interview

curl -X POST $BASE/api/v1/interviews \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"title":"Support role screen","category":"Hiring","questions":["Tell us about yourself","Why this role?"]}'

Create a full interview in one call

curl -X POST $BASE/api/v1/interviews -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{
  "title":"Barista screen","category":"Hiring","intro":"Thanks for applying!",
  "questions":[
    "Why do you want to work here?",
    {"text":"Tell us about a busy shift.","audio_url":"https://example.com/q2.mp3"},
    {"audio_base64":"<base64 audio>"}   // no text: auto-transcribed if a server STT key is set
  ]}'

Create from voice (draft)

Send a dictation audio file and/or transcript. Say "Title …", "Category …", "Intro …", "Question one …", "Next question …", "Delete last question", "Done". Returns a draft interview.

curl -X POST $BASE/api/v1/interviews/from-voice -H "Authorization: Bearer $KEY" -F audio=@dictation.webm
curl -X POST $BASE/api/v1/interviews/from-voice -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" -d '{"transcript":"Title Barista screen category hiring question one why coffee next question what shifts can you work done"}'

Add a question

curl -X POST $BASE/api/v1/interviews/$ID/questions -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" -d '{"text":"What is your availability?"}'

Add a question with audio (multipart, auto-transcribed)

curl -X POST $BASE/api/v1/interviews/$ID/questions -H "Authorization: Bearer $KEY" -F audio=@question.webm [-F text="optional text"]

Edit & delete

curl -X PATCH  $BASE/api/v1/interviews/$ID -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"title":"New title","draft":false}'
curl -X DELETE $BASE/api/v1/interviews/$ID -H "Authorization: Bearer $KEY"
curl -X PATCH  $BASE/api/v1/interviews/$ID/questions/$QID -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"text":"Reworded?","position":0}'
curl -X DELETE $BASE/api/v1/interviews/$ID/questions/$QID -H "Authorization: Bearer $KEY"

Create a recipient link (uses 1 credit)

curl -X POST $BASE/api/v1/interviews/$ID/recipients -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" -d '{"name":"Alex","email":"alex@example.com"}'
# → {"id":"…","status":"sent","link":"https://…/r/…"}

List interviews and statuses

curl $BASE/api/v1/interviews -H "Authorization: Bearer $KEY"
curl $BASE/api/v1/interviews/$ID -H "Authorization: Bearer $KEY"

Get answers, transcripts, audio

curl $BASE/api/v1/recipients/$RID -H "Authorization: Bearer $KEY"
# → {"status":"completed","answers":[{"question":"…","transcript":"…","audio_url":"…"}]}

Recipient API: answering (bots welcome)

Authenticate with the recipient link token (the last part of /r/TOKEN). No account needed. API answers are marked responder_type: "bot" and show a 🤖 Bot response badge to the sender. If the sender also passes their own API key, they may set "responder_type":"human" (e.g. relaying a phone interview). Senders can turn off "Allow bot responses" per interview (PATCH {"allow_bots":false}). Limit: 30 requests/min per link. Webhooks fire exactly as for human answers.

Get questions

curl $BASE/api/v1/respond/$TOKEN

Answer (text, batch)

curl -X POST $BASE/api/v1/respond/$TOKEN/answers -H "Content-Type: application/json" \
  -d '{"answers":[{"question_id":"Q1","text":"My answer"},{"question_id":"Q2","audio_base64":"<base64>"}]}'

Answer with an audio file (auto-transcribed if server STT is configured)

curl -X POST $BASE/api/v1/respond/$TOKEN/answers -F question_id=Q1 -F audio=@answer.webm [-F text="optional transcript"]

Submit

curl -X POST $BASE/api/v1/respond/$TOKEN/submit -H "Content-Type: application/json" -d '{}'
# 400 lists unanswered question ids; pass {"allow_partial":true} to submit anyway. 409 if already submitted.

Proctored interviews

Create with "proctoring":{"enabled":true,"strict":false,"snapshots":false,"awaySec":3,"roomScan":true,"rearCheck":true,"mirrorCheck":false,"idCheck":false,"phoneStand":true,"spotChecks":false,"motion":true} (or PATCH it). Proctored interviews reject bot/API answers. Recipient and webhook payloads include proctoring: integrity_score (0–100), timestamped flags (type, label, question_id, duration_sec, snapshot_url), and room scans (kind, url, detected objects). Flags are signals for human review, not proof.

Webhooks

Set a URL in Dashboard → Developer. Events: answer.submitted, interview.completed. Verify X-EchoAnswer-Signature = HMAC-SHA256(body, signing secret).

{"event":"interview.completed","interview_id":"…","recipient":{"id":"…","name":"Alex","status":"completed"},"answers":[…],"at":"2026-10-17T19:40:00Z"}

Embed the recipient experience

<script src="$BASE/embed.js" data-token="RECIPIENT_TOKEN"></script>
<!-- or -->
<iframe src="$BASE/r/RECIPIENT_TOKEN" allow="microphone" style="width:100%;height:640px;border:0"></iframe>

Coming soon

Sandbox keys (ea_test_), Zapier & Make apps, Greenhouse / Lever ATS, Slack notifications.

Recording consent & require_audio

Every answer on every channel is stored as audio plus a transcript. Before answering, a recipient must accept the notice "This session is recorded and transcribed". Senders cannot turn this off. API respondents call POST /api/v1/respond/:token/consent {"recording_consent":true} first; the time, IP and user agent are logged. require_audio defaults to true, so text-only API answers get a 400. If you set it to false, text-only answers are accepted and flagged no_audio:true. Consent records appear in recipient objects, exports and webhooks as recording_consent.

Question types & scoring

Each question has a type: open|scale|stars|nps|yesno|choice|multi|likert. Optional fields are min, max, min_label, max_label, options, required, and comment (none|optional|required). Answers carry a value plus audio. The sender scores answers 1–10 and can apply a weighted rubric:

curl -X PUT $BASE/api/v1/recipients/RID/review -H "Authorization: Bearer KEY" -H "Content-Type: application/json" \
 -d '{"answerScores":{"QID":8},"criteria":{"Communication":7},"notes":"Strong"}'

Templates

# list (built-ins + yours)
curl $BASE/api/v1/templates -H "Authorization: Bearer KEY"
# create from JSON, or upload CSV/XLSX/DOCX/TXT/JSON (multipart "file"); create_interview=true also creates an interview
curl -X POST $BASE/api/v1/templates -H "Authorization: Bearer KEY" -F file=@questions.csv -F create_interview=true
curl -X POST $BASE/api/v1/templates/TEMPLATE_ID/interviews -H "Authorization: Bearer KEY"

CSV columns: text,type,min,max,options,required,comment (options separated by |). Sample: /sample-template.csv.

Batch sending & campaigns

curl -X POST $BASE/api/v1/interviews/ID/recipients/batch -H "Authorization: Bearer KEY" -H "Content-Type: application/json" -d '{
 "name":"Q3 reviews","dry_run":false,"scheduled_at":"2026-11-01T16:00:00Z","due_at":"2026-11-08T23:59:00Z",
 "reminders":{"every_days":2,"max":3},"send_email":true,
 "recipients":[{"name":"Ana Ruiz","email":"ana@ex.com","phone":"+15551230001","fields":{"role":"Engineer","manager":"Sam"}}]}'

You can also upload a file with -F file=@recipients.csv (columns name,email,phone plus any custom columns, used as {{first_name}}, {{role}}, {{manager}} and so on). Each recipient costs 1 credit. Send dry_run:true to get a quote. If credits run short or the Pay As You Go cap is reached, you get a 402. Each recipient gets a unique link and a 6-digit phone PIN. Email goes out only when RESEND_API_KEY or SMTP_URL is configured; otherwise use the links CSV. Sample: /sample-recipients.csv.

curl $BASE/api/v1/campaigns -H "Authorization: Bearer KEY"
curl "$BASE/api/v1/campaigns/CID/report?filter_role=Engineer&ai=1" -H "Authorization: Bearer KEY"   # JSON; format=csv|xlsx
curl -X POST $BASE/api/v1/campaigns/CID/remind -H "Authorization: Bearer KEY"

The report includes completion, per-question averages and distributions, NPS, a leaderboard, low scores and outliers, segments, a trend across recurring sends, and themes and sentiment (OpenAI if OPENAI_API_KEY is set, otherwise a local lexicon). In anonymous mode, identities are hidden and segments smaller than 3 are suppressed.

Branding

curl -X PATCH $BASE/api/v1/profile -H "Authorization: Bearer KEY" -H "Content-Type: application/json" \
 -d '{"org_name":"Acme","display_name":"Acme People Team","color":"#C9A23A","welcome":"Hi {{first_name}}!","thank_you":"Thanks!","phone_greeting":"Thanks for calling Acme."}'

Branding applies to the answer page, emails and the phone flow. The phone greeting plays before the recording notice, which is always played.

Phone answering (Twilio)

Set TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN and TWILIO_PHONE_NUMBER, then point the number's voice webhook to POST $BASE/twilio/voice. Until these are set, phone endpoints return 503. Callers hear the recording notice, enter their PIN, hear the sender's greeting, and press 1 to consent (no consent ends the call). Questions are read aloud, or the recorded question audio is played. Open answers are recorded, and the caller presses 1 to keep or 2 to re-record. Scored questions take keypad input followed by a required spoken confirmation. Answers have channel:"phone" and responder_type:"phone", are transcribed, and trigger webhooks. Proctored interviews cannot be answered by phone. Every request's Twilio signature is validated. To give an interview its own number: PATCH {"phone_number":"+1..."}. To call someone now or later: POST /api/v1/recipients/RID/call {"at":"2026-11-01T17:00:00Z"}. Each answer uses 1 credit, plus an optional PHONE_SURCHARGE_PER_MIN.

Zapier, Make & webhooks

Use a "Webhooks by Zapier → Catch Hook" trigger and paste its URL as your webhook (Developer → Webhook). Events: answer.submitted, interview.completed. Each payload includes the interview id, recipient, answers (transcript, audio file, value), recording consent and proctoring report, signed with your webhook secret. For actions, use "Webhooks by Zapier → Custom Request" with your API key against the endpoints below. Slack: paste an incoming-webhook URL in Settings to get a message with an AI summary when someone finishes. Calendar: set a Calendly (or any https) link in Settings to show a "Book your next step" button on the thank-you page.

Script import

curl -H "Authorization: Bearer KEY" -F file=@script.pdf $BASE/api/v1/import-script
curl -H "Authorization: Bearer KEY" -H "Content-Type: application/json" -d '{"url":"https://docs.google.com/document/d/ID/edit"}' $BASE/api/v1/import-script

Accepts text, a file (PDF, DOCX, TXT, MD, RTF, or audio, which is transcribed), or a public Google Doc link. Returns {title,intro,sections,questions[]} for preview. Nothing is saved. Numbered or bulleted lines and sentences ending in "?" become questions, headings become sections, and scales, yes/no and choices are detected.

Branching, limits & captions

PATCH $BASE/api/v1/interviews/ID/questions/QID/logic
{"branch":[{"if":{"op":"lt","value":5},"goto":"QID_FOLLOWUP"},{"if":{"op":"contains","value":"price|cost"},"goto":"end"}],
 "retakes":1,"timeLimit":120,"practice":false,"caption":"Text shown with the question audio/video"}

Ops: lt, lte, gt, gte, eq, neq, contains (keywords split by |, matched against the transcript), answered. Answer responses include next_question_id, next_index (-1 = finished) and progress. Going over the retake limit returns 429. Going over the time limit (plus a 15s grace period) returns 408. Submit accepts answers along the branched path.

Languages, summaries, privacy

GET /api/languages. Recipients pick their language on the answer page. Questions and phone prompts (Twilio voice and speech language) follow it. Answer transcripts are stored in the original language and auto-translated into the sender's language when OPENAI_API_KEY is set. Otherwise they're flagged untranslated:true. Summaries: GET /api/v1/recipients/RID/summary and GET /api/v1/interviews/ID/summary, which use AI when a key is configured and a keyword/sentiment summary otherwise. Retention: Settings → auto-delete recordings after N days (7 or more; transcripts are kept). Recipients can request deletion from the thank-you page (POST /api/r/TOKEN/delete-request), and owners/admins erase the data from Settings. Every change made by the team is written to the audit log. Recording consent and a voice recording are always required. Video answers are optional and only added on top of audio.