Skip to main content

Interviews

The lifecycle, from creation to a scored report.

Expert Hire developer workflow for Interviews
Build against the same evidence trail the product uses.

Lifecycle

create ──> Scheduled ──> In Progress ──> Processing ──> Completed
                │                                          │
                └──────────> Cancelled                   report
StatusMeaning
ScheduledCreated, nobody has joined
In ProgressCandidate is in the room now
ProcessingInterview ended, scoring is running
CompletedReport is ready
CancelledCancelled, by you or by us

Creating

You can pass an existing candidate_id, or create the candidate inline:

{
  "candidate": { "email": "[email protected]", "name": "Ada Lovelace" },
  "round_type": "general_interview",
  "difficulty": 3,
  "role": "Backend Engineer"
}

difficulty runs 1 to 5. role drives the questions, so make it the real job title rather than an internal code.

An email that already belongs to an Expert Hire account is rejected with 400. That is deliberate: adopting it would attach your interview to somebody's personal prep history.

Candidates

Persist the candidate_id you get back. If you lose it, look the person up by email rather than creating a duplicate:

curl "https://prep-api.experthire.cloud/v1/[email protected]" \
  -H "Authorization: Bearer $EH_SECRET_KEY"

Still a placeholder: $EH_SECRET_KEY. Add it under Your values above.

GET /v1/candidates also pages the whole list, newest first. PATCH corrects a typo in a name or email.

Deleting is erasure, and it is not reversible:

curl -X DELETE https://prep-api.experthire.cloud/v1/candidates/$CANDIDATE_ID \
  -H "Authorization: Bearer $EH_SECRET_KEY"

Still a placeholder: $CANDIDATE_ID, $EH_SECRET_KEY. Add them under Your values above.

The person is de-identified rather than dropped. Name, email, phone and every profile field are overwritten; interviews and reports keep their candidate id, so your own counts and averages still reconcile, but the record can no longer be tied to a person.

PATCH and DELETE only work on a candidate you created through the API. One who has since signed in to Expert Hire owns their own profile and deletion, and returns candidate_not_editable. Rewriting their email would otherwise hand someone else their sign-in.

Listing

curl -G https://prep-api.experthire.cloud/v1/interviews \
  -H "Authorization: Bearer $EH_SECRET_KEY" \
  --data-urlencode "limit=50"

Still a placeholder: $EH_SECRET_KEY. Add it under Your values above.

{
  "object": "list",
  "data": [{ "object": "interview", "id": "…", "status": "Completed" }],
  "has_more": true,
  "next_cursor": "b9ec0ced-…"
}

Newest first. Pass status to filter. Page by setting starting_after to next_cursor, which is the last id on the page you just read:

curl -G https://prep-api.experthire.cloud/v1/interviews \
  -H "Authorization: Bearer $EH_SECRET_KEY" \
  --data-urlencode "starting_after=$NEXT_CURSOR"

Still a placeholder: $EH_SECRET_KEY, $NEXT_CURSOR. Add them under Your values above.

The cursor is a position in the list, not a page number. That matters if you poll: with an offset, an interview created between two requests shifts everything down a row and the next page silently repeats or skips one. Paging from the last id you saw cannot.

Listing is not how you find out an interview finished. Poll it and you will either hammer us or miss the transition. Register a webhook and listen for interview.completed, then fetch the report.

Scheduling into capacity

Interviews run against a shared pool, so a slot can fill. Ask before you commit a time rather than discovering it when the candidate tries to join:

curl -G https://prep-api.experthire.cloud/v1/interviews/slots \
  -H "Authorization: Bearer $EH_SECRET_KEY" \
  --data-urlencode "from=$(date +%s)"

Still a placeholder: $EH_SECRET_KEY. Add it under Your values above.

{ "object": "list", "data": [{ "start_time": 1786351800, "available": 500 }] }

One entry per half hour. from and to are unix seconds, the default window is the next 24 hours, and the maximum is 14 days. Pass candidate_id and slots that candidate already holds are left out.

Attaching a resume

Resumes make the interview specific rather than generic. Upload, register and score it first, then pass resume_id when you create the interview. The interviewer reads the parsed resume and asks about what is in it.

The resume has to belong to the same candidate. One that belongs to another candidate is rejected with 404 resume_not_found, so a mismatched id fails rather than quietly interviewing against somebody else's history.

Uploading a file is not enough on its own: the resume has to be registered before anything can use it. The full flow is in resumes.

Getting the candidate in

Three ways, covered in frontend integration:

  • Hosted link. We host the room, you email a URL. Least work.
  • Embedded. The room runs inside your page. No redirect for the candidate.
  • Headless. You build the room. Most control, and you give up the most.

The billable moment

A credit is reserved when you create an interview and committed when the candidate joins. Joining is what dispatches the AI interviewer and starts recording, so it is the point where the interview genuinely costs something.

Cancelling an interview nobody joined returns the credit:

curl -X POST https://prep-api.experthire.cloud/v1/interviews/$ID/cancel \
  -H "Authorization: Bearer $EH_SECRET_KEY"

Still a placeholder: $EH_SECRET_KEY. Add it under Your values above.

Cancelling one that already started does not. Creating interviews speculatively is safe; letting candidates join them is what you pay for.

Round types

round_type selects the interview style. The catalogue is per-org and some are module-gated, so read yours rather than guessing:

curl https://prep-api.experthire.cloud/v1/round-types \
  -H "Authorization: Bearer $EH_SECRET_KEY"

Still a placeholder: $EH_SECRET_KEY. Add it under Your values above.

Each entry carries supports_code_editor and supports_whiteboard. GET /v1/companies, /v1/jobs and /v1/coding-languages are the other lookups worth reading at boot.

Commonly available: general_interview, coding, hr_round, system_design, communication, problem_solving_or_aptitude, vc_pitch.

There is no technical round, and no general (it is general_interview). daily_english is not bookable over the API: it is the prep student app's own daily drill, so GET /v1/usage may report the module while the round type is refused. An unknown slug is rejected with 400 invalid_round_type, and the error message lists the slugs your organisation has, so you never have to guess.

Whether a round offers a code editor or whiteboard comes back on the join grant as supports_code_editor and supports_whiteboard, so you do not have to hardcode a mapping that will drift.