Phone screens
An AI interviewer calls the candidate on their phone, over your own SIP trunk.
A phone_screen is an AI interview conducted over the telephone. There is no browser,
no link and nothing for the candidate to install: we dial their number, the interviewer
speaks to them, and you get the same transcript and report any other interview produces.
It suits the candidates a link does not reach. People who will take a call but will not sit in front of a webcam, roles hired at volume, and the first pass where you mostly need to confirm interest, availability and a few facts.
GET /v1/assessment-types lists it with its credit cost. It needs the AI Voice Calls
module on your organization, which we turn on for you once a trunk is connected.
You bring the number
We never supply a phone number. Calls are placed over your own SIP trunk, on your own number, so the candidate sees a number they recognize and your carrier bills you for the minutes.
Connecting a trunk is not an API step, and it is not self-serve yet. Send us your carrier details and we set it up with you: it needs credentials from your carrier and an allowlist entry on their side so our calls are accepted. Twilio, Plivo, Vonage and any standards-compliant SIP trunk all work, because every one of them is reached over plain SIP.
Once it is connected, read your numbers:
curl https://hiring-api.experthire.cloud/v1/phone-numbers \
-H "Authorization: Bearer $EH_KEY"
Still a placeholder: $EH_KEY. Add it under Your values above.
{
"object": "list",
"data": [
{
"object": "phone_number",
"id": "0f9c2a71-3e5b-4a18-9d64-7c1b2e8f5a03",
"phone": "+14155550100",
"label": "US recruiting line",
"provider": "twilio",
"inbound_enabled": false,
"outbound_enabled": true,
"registered": true,
"created_at": 1767225601
}
],
"has_more": false,
"total_count": 1
}
A number that is not registered or not outbound_enabled cannot place a call.
Create one
The candidate needs a phone number in international format. Send it on the candidate, or override it per call later.
curl -X POST https://hiring-api.experthire.cloud/v1/assessments \
-H "Authorization: Bearer $EH_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"type": "phone_screen",
"job_id": "'$JOB_ID'",
"candidate": {
"name": "Jane Doe",
"email": "[email protected]",
"phone": "+14155550123"
},
"duration_minutes": 15
}'
Still a placeholder: $EH_KEY, $JOB_ID. Add them under Your values above.
duration_minutesis 5 to 60, and 15 if you leave it out. A phone screen is shorter than a video interview by design.questionsworks as it does on an AI interview: your list becomes the pool the interviewer draws from.round_typeandsend_inviteare refused. The shape is fixed, and there is no link to email.
phone is written to the candidate only when they do not already have one, so naming an
existing candidate never overwrites a number one of your recruiters corrected. Patch it
deliberately with PATCH /v1/candidates/{id}.
Creating the assessment does not call anybody. Nothing is dialed until you say so.
Place the call
curl -X POST https://hiring-api.experthire.cloud/v1/assessments/$ID/call \
-H "Authorization: Bearer $EH_KEY" \
-H "Content-Type: application/json" \
-d '{}'
Still a placeholder: $EH_KEY. Add it under Your values above.
With one outbound number connected you can leave the body empty: we call the candidate's
number from the only number that can dial. Name phone_number_id once you have more than
one, and to to call a different number for this attempt only.
The call returns as soon as it is placed, not when it is answered:
{
"object": "call",
"id": "5d3a6c19-8b2f-4e70-a1c4-92f6b0e7d518",
"assessment_id": "3b8e1d02-5c77-4c2a-8a41-9b2f7e6d4c10",
"status": "dialing",
"from": "+14155550100",
"to": "+14155550123",
"attempt": 1,
"duration_seconds": 0,
"credits_spent": 0,
"created_at": 1767225601
}
Nobody picks up the first time
That is the normal case, so it is worth building for.
GET /v1/assessments/{id}/calls returns every attempt with how it ended. Call again
when one goes unanswered. Each attempt is its own row; the assessment stays open.
| Disposition | What happened | Call again? |
|---|---|---|
answered | They picked up and the interview ran | No |
no_answer | It rang out | Yes, later |
busy | The line was engaged | Yes, shortly |
rejected | They declined it | Once, then stop |
voicemail | It reached an answering machine | Yes, at a different hour |
invalid_number | The number is not reachable | No. Fix the number first |
trunk_unreachable | Your trunk did not accept the call | Check the trunk, not the candidate |
failed | The call could not be placed | Check the trunk |
What a screen costs
A phone screen costs credits once, when the candidate answers, however many attempts it took to reach them. A call that rings out, hits a busy line, reaches voicemail or is declined costs nothing.
credits_spent on each attempt tells you which one carried the charge, and at most one
per assessment ever does. Creating the assessment charges nothing, and cancelling it
refunds nothing, because nothing was taken.
What you get back
The same things a browser interview produces: GET /v1/assessments/{id}/report for the
score and feedback, GET /v1/assessments/{id}/transcript for what was said. A phone
screen is written up and scored through the same path an ai_interview uses, so the
webhooks you already handle fire for it too, keyed on the same assessment id.
Two differences worth knowing. There is no video, so the report carries no visual
signals. And a phone screen has no room to embed: POST /v1/sessions and
POST /v1/assessments/{id}/launch-link are refused on one with unsupported_operation.
Ending a call
The interviewer ends the call itself when the screen is done. POST /v1/assessments/{id}/cancel
hangs up a call that is still running and closes the assessment. A candidate who hangs up
early ends the call, and the interview is written up from what was said.
Sandbox
A sandbox key can create a phone screen, list its attempts and receive its webhooks, so
you can build the whole flow without spending anything. It will not dial: the call
endpoint returns sandbox_cannot_call. Use a live key to reach a real phone.
When it will not dial
| Code | Status | What to do |
|---|---|---|
candidate_phone_required | 400 | The candidate has no number. Set one, or pass to |
invalid_phone | 400 | Use international format, for example +919876543210 |
phone_number_required | 400 | More than one of your numbers can dial. Name one |
no_calling_number | 409 | No trunk is connected yet |
call_not_placeable | 409 | Already on a call, or the candidate asked not to be called |
sandbox_cannot_call | 400 | Sandbox never dials. Use a live key |
agent_unavailable | 503 | The interviewer is not ready. Retry shortly |
We refuse rather than dial when anything is wrong. A refused call creates no attempt and costs nothing.
Next
- Assessments: shaping the interview and its questions
- Reports: the fields you get back
- Webhooks: what fires, and when