Skip to main content

Reports

What comes back, and what to ignore.

GET /v1/assessments/{id}/report returns the scored result once the assessment has been scored. Before that it is a 404, deliberately, rather than a zero-scored report for something nobody has taken.

In sandbox this returns the real shape with fixed numbers and "sample": true. Sandbox never records, so nothing can be scored there; rather than a 404 you can build nothing against, you get the response you will have to parse. Never show a sample to a human, and never store its scores.

Do not gate on status == "completed". Shortlisting or rejecting a candidate overwrites the status in place, so a scored assessment often reads shortlisted or rejected. The report is available for all three.

The envelope

Both assessment types return the same envelope. The type-specific detail sits in feedback for an AI interview and in screening for a resume screen.

200AI interview, trimmed
{
  "object": "assessment_report",
  "assessment_id": "3b8e1d02-5c77-4c2a-8a41-9b2f7e6d4c10",
  "type": "ai_interview",
  "status": "completed",
  "livemode": true,
  "score": 68,
  "outcome": "good_fit",
  "summary": "Answered clearly with concrete examples, hesitant on trade-offs.",
  "question": "Walk me through a system you owned end to end.",
  "feedback": { "...": "see below" },
  "recording_url": "https://...",
  "transcript_url": "https://...",
  "created_at": 1767229200
}

outcome is good_fit, unlikely_fit or incomplete. It is empty until the scoring pipeline has run, which is the most reliable "is this scored yet" check.

Resume screens

A resume_screen carries screening instead of feedback. Every number is 0-100.

200Resume screen, trimmed
{
  "object": "assessment_report",
  "assessment_id": "9d41c7b8-2e35-4a19-b6f0-1c8a5d3e2f47",
  "type": "resume_screen",
  "status": "completed",
  "livemode": true,
  "score": 74,
  "outcome": "good_fit",
  "screening": {
    "ats_readiness": 81,
    "readability": 76,
    "experience_relevance": 72,
    "formatting": 68,
    "domain_skill_fit": 79,
    "must_have_coverage": 83,
    "good_to_have_coverage": 55,
    "weighted_skill_score": 75,
    "strengths": "Six years on payments infrastructure.",
    "weaknesses": "No evidence of team leadership."
  },
  "created_at": 1767229200
}

must_have_coverage and good_to_have_coverage are the percentage of each skill group the resume evidences. weighted_skill_score combines them at 70/30.

AI interview feedback

The report carries an overall score, a written summary, and a feedback object with the detail.

Three arrays inside feedback use different key names, which is the most common thing to get wrong:

ArrayKeysScale
speech_analysiscriteria, rating, comments1-100
behavioural_analysislabel, score, outcome0-100
field_knowledgecriteria, score, comments0-100

Note rating on one and score on the others. They are genuinely different fields, not one field described two ways.

200The three arrays, side by side
{
  "speech_analysis": [
    { "criteria": "Fluency", "rating": 72, "comments": "Steady pace, few fillers." }
  ],
  "behavioural_analysis": [
    { "label": "Posture", "score": 61, "outcome": "Composed" }
  ],
  "field_knowledge": [
    { "criteria": "Handling ambiguity", "score": 61, "comments": "One concrete example." }
  ]
}

Things that will mislead you

The audio-only sentinel. When there is no video, all three behavioural_analysis entries come back at score: 0 with outcome: "Not Analyzed (Audio Only)". That is "we did not measure this", not "this candidate scored zero". Check the outcome before rendering a number.

field_knowledge criteria are generated per transcript. The names differ between candidates, so they are readable but not comparable. Do not key a rubric off them.

Integrity signals travel with the scores. feedback also carries proctoring data. If you re-render the report to a candidate or a client, allowlist the keys you want rather than passing the object through.

A zero code_score does not mean nothing was written. Before scoring we check the submission is a genuine attempt at the question that was asked. Code that does not engage with it scores 0 across the board with the reason "No code available for evaluation", even though code.source is populated and comes back to you. Read code.summary to tell the two apart: it says outright when the submission was not a real attempt.

An interview round stores code, it does not run it. code_score is a model reading the submission against the question. Nothing is executed against test cases: that is what a coding_test assessment is for. code carries source, language and summary, and only the final state. Saves are last-write-wins, so there is no revision history and no per-submission timestamp.

Media

Recording and transcript URLs are signed and expire in two hours. Do not store them. Store the assessment id and re-fetch.

A field is absent when the artefact was never produced, which is normal for an assessment that ended early.

Transcript and recording

The report carries presigned recording_url and transcript_url links that expire in two hours, which is no use once you have stored the report. Two endpoints exist for reading them later:

  • GET /v1/assessments/{id}/transcript returns the turns themselves, in order, as JSON. Sandbox never records, so it returns a short fixed sample with sample: true.
  • GET /v1/assessments/{id}/recording-url mints a fresh link each call and tells you whether the file is watermarked, which matters if you pass it on to a client.

What the room observed

GET /v1/assessments/{id}/events returns the proctoring signals, oldest first. ?category=integrity drops the benign activity markers and keeps the rest. An event name we do not recognise counts as an integrity signal rather than benign, so a new client cannot quietly hide something.

Do not confuse this with /v1/events, which is the webhook delivery log.

Recording your decision

Reading a report does not change anything. When you have decided, tell us:

curl -X POST https://hiring-api.experthire.cloud/v1/assessments/$ID/decision \
  -H "Authorization: Bearer $EH_KEY" \
  -d '{"outcome": "shortlisted"}'

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

outcome is shortlisted or rejected. It is refused with report_not_ready until the assessment has been scored.

This records the decision so it shows up for the recruiters working the role. It sends no email and moves nobody to a next round, because an assessment created over this API is a single round. Telling the candidate is yours to do.

Next