API reference
API reference
Read your interviews, candidates, transcripts, recordings and files from your own tools. Every endpoint and every field, in plain words.
Base address https://api.lessrounds.ai/v1 · Updated October 3, 2026
Before you start
-
The API only reads data. Nothing outside LessRounds can change statuses, invite candidates or delete anything.
-
Every address starts with
https://api.lessrounds.ai/v1. -
Create an API key in Settings → API & webhooks. Only admins can, and each key is shown once. Send it in the
Authorizationheader on every request, never in the address:curl https://api.lessrounds.ai/v1/me \ -H "Authorization: Bearer lr_live_…" -
Keep keys on a server or inside your automation tool. Anyone with a key can read your candidates’ data. If one leaks, revoke it in the same place: it stops working on the next request.
Answers
Every answer is JSON. Successful ones look like this, with the result inside data:
{
"success": true,
"data": { "candidate": { "id": "V1StGXR8_Z5jdHi6Bmy2k", "name": "Riya Sharma" } },
"timestamp": 1791019260,
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
- Times are in UTC, like
2026-10-03T09:31:40Z. - A missing value is
null, never left out, so your tool always sees every field. - Answers to form questions are always text. Yes/no answers are
"Yes"or"No".
Pages of results
Lists come back a page at a time. limit sets the page size (25 by default; at most 25 for candidates and events, 100 for interviews). Each page has a next_cursor: pass it back as cursor to get the next page. It’s null on the last page.
Errors
| Status | What it means | What to do |
|---|---|---|
| 401 | The key is missing, wrong or revoked. | Check the Authorization header and the key. |
| 403 | Your company account is suspended. | Contact us. |
| 404 | There’s no such item in your company. | Check the id. |
| 422 | A filter isn’t valid. details.problems lists each problem. | Fix the query. |
| 429 | Too many requests. | Wait the number of seconds in the Retry-After header, then try again. |
Limits
- 120 requests a minute for each key, and 300 for your whole company.
- The API and webhooks are free. They use no credits.
- The change history (
GET /events) keeps 30 days.
Changes to the API
/v1 is a promise. We may add fields, endpoints and event types at any time, so ignore anything you don’t recognize. If we ever remove or rename something, it will be in a new version (/v2), announced at least 90 days ahead.
Postman, Make and n8n
Tools that read API descriptions can import https://api.lessrounds.ai/v1/openapi.json. It needs no key.
Endpoints
Every endpoint can also answer 401, 403 or 429, as explained under Errors.
Check your key
GET/v1/me
Returns your company and this key's name. Use it to test a connection.
What’s in data
| Field | Type | What it is |
|---|---|---|
| company | object | |
| company.id | string | |
| company.name | string | |
| api_key | object | |
| api_key.id | string | |
| api_key.name | string |
Example
curl https://api.lessrounds.ai/v1/me \
-H "Authorization: Bearer lr_live_…"List interviews
GET/v1/interviews
Newest first. Deleted interviews are never listed.
Parameters
| Name | Type | What it does |
|---|---|---|
| statusoptional | string | Only interviews with this status.One of: draft, active, paused, closed |
| limitoptional | integer | How many to return (1–100, default 25). |
| cursoroptional | string | The next_cursor of the previous page. |
What’s in data
| Field | Type | What it is |
|---|---|---|
| interviews | list of Interview | |
| next_cursor | string or null | Pass as cursor to get the next page; null on the last page. |
422: A query parameter isn't valid; details.problems lists each one.
Example
curl https://api.lessrounds.ai/v1/interviews \
-H "Authorization: Bearer lr_live_…"Get an interview
GET/v1/interviews/{id}
One interview with its steps: form questions (their ids are the keys of a candidate's answers), the AI interview and the document asked for.
Parameters
| Name | Type | What it does |
|---|---|---|
| idin the address | string | The id. |
What’s in data
| Field | Type | What it is |
|---|---|---|
| interview | Interview |
404: Nothing with this id in your company.
Example
curl https://api.lessrounds.ai/v1/interviews/INTERVIEW_ID \
-H "Authorization: Bearer lr_live_…"List candidates
GET/v1/candidates
Candidate attempts as full candidate objects. By default only attempts the candidate submitted, most recently submitted first. With state=in_progress or state=all, newest started first. The order never changes while you page, so paging with next_cursor never skips anyone. Team members' previews are never listed.
Parameters
| Name | Type | What it does |
|---|---|---|
| interview_idoptional | string | Only this interview's candidates. |
| statusoptional | string | One or more statuses, separated by commas: in_progress, passed, needs_review, did_not_pass, shortlisted, on_hold, rejected, cancelled, abandoned. |
| stateoptional | string | submitted (default): finished every step. in_progress: still taking it. all: everything, including cancelled and unfinished attempts.One of: submitted, in_progress, all |
| submitted_afteroptional | time | Only candidates who submitted at or after this time (RFC 3339). |
| submitted_beforeoptional | time | Only candidates who submitted before this time (RFC 3339). |
| limitoptional | integer | How many to return (1–25, default 25). |
| cursoroptional | string | The next_cursor of the previous page. |
What’s in data
| Field | Type | What it is |
|---|---|---|
| candidates | list of Candidate | |
| next_cursor | string or null | Pass as cursor to get the next page; null on the last page. |
422: A query parameter isn't valid; details.problems lists each one.
Example
curl https://api.lessrounds.ai/v1/candidates \
-H "Authorization: Bearer lr_live_…"Get a candidate
GET/v1/candidates/{id}
One candidate attempt, in any state.
Parameters
| Name | Type | What it does |
|---|---|---|
| idin the address | string | The id. |
What’s in data
| Field | Type | What it is |
|---|---|---|
| candidate | Candidate |
404: Nothing with this id in your company.
Example
curl https://api.lessrounds.ai/v1/candidates/CANDIDATE_ID \
-H "Authorization: Bearer lr_live_…"Get the interview transcript
GET/v1/candidates/{id}/transcript
The AI interview, question by question. [] when there's no AI interview yet.
Parameters
| Name | Type | What it does |
|---|---|---|
| idin the address | string | The id. |
What’s in data
| Field | Type | What it is |
|---|---|---|
| transcript | list of TranscriptEntry |
404: Nothing with this id in your company.
Example
curl https://api.lessrounds.ai/v1/candidates/CANDIDATE_ID/transcript \
-H "Authorization: Bearer lr_live_…"Get a download link for the interview recording
GET/v1/candidates/{id}/recording
Returns a link that works for 15 minutes. 404 when there's no recording.
Parameters
| Name | Type | What it does |
|---|---|---|
| idin the address | string | The id. |
What’s in data
| Field | Type | What it is |
|---|---|---|
| recording | RecordingDownload |
404: Nothing with this id in your company.
Example
curl https://api.lessrounds.ai/v1/candidates/CANDIDATE_ID/recording \
-H "Authorization: Bearer lr_live_…"Get the change history
GET/v1/events
What happened to your candidates, oldest first, for the last 30 days: candidate.submitted, candidate.scored, candidate.ready and candidate.status_changed. Save next_cursor and pass it back as after next time to get only what's new — it's returned even when nothing happened, and has_more says whether to ask again straight away. An event usually appears here within seconds of happening, and always within about a minute.
Parameters
| Name | Type | What it does |
|---|---|---|
| afteroptional | string | The next_cursor of your previous call. Leave out to start from the oldest kept event. |
| typeoptional | string | One or more event types, separated by commas. |
| interview_idoptional | string | Only this interview's candidates. |
| limitoptional | integer | How many to return (1–25, default 25). |
What’s in data
| Field | Type | What it is |
|---|---|---|
| events | list of Event | |
| next_cursor | string | Pass as after next time. |
| has_more | boolean | More events are waiting right now. |
422: A query parameter isn't valid; details.problems lists each one.
Example
curl https://api.lessrounds.ai/v1/events \
-H "Authorization: Bearer lr_live_…"Get a download link for a candidate's document
GET/v1/files/{id}
For files in a candidate's documents list. Returns a link that works for 15 minutes.
Parameters
| Name | Type | What it does |
|---|---|---|
| idin the address | string | The id. |
What’s in data
| Field | Type | What it is |
|---|---|---|
| file | FileDownload |
404: Nothing with this id in your company.
Example
curl https://api.lessrounds.ai/v1/files/FILE_ID \
-H "Authorization: Bearer lr_live_…"Objects
The shapes the endpoints and webhooks use. Fields are always present; a missing value is null.
Candidate
One attempt at an interview.
| Field | Type | What it is |
|---|---|---|
| id | string | |
| attempt | integer | 1 for the first attempt; a retake is a new candidate with attempt 2. |
| name | string | |
| string | ||
| phone | string or null | |
| interview | InterviewRef | |
| status | string | One of: in_progress, passed, needs_review, did_not_pass, shortlisted, on_hold, rejected, cancelled, abandoned |
| status_label | string | |
| rejection_reason | string or null | |
| started_at | time | |
| submitted_at | time or null | When the candidate finished every step. Null while they're still taking it. |
| result | Result | |
| ai_interview | AIInterview or null | Null when the interview has no AI step, or it hasn't started. |
| answers | map of Answer | Form answers keyed by the question's id (see the interview's steps.form.questions). The id never changes, even if the question is reworded. |
| documents | list of Document | |
| dashboard_url | string | The candidate's page in LessRounds (needs a LessRounds sign-in). |
InterviewRef
| Field | Type | What it is |
|---|---|---|
| id | string | |
| title | string |
Result
| Field | Type | What it is |
|---|---|---|
| state | string | waiting: not final yet (still taking it, or the score or recording is on its way). complete: the AI score and recording are in (the same moment credits are charged) — this can happen before the candidate finishes a later step. incomplete: final, but something is missing (see incomplete_reason). not_applicable: the interview has no AI step.One of: waiting, complete, incomplete, not_applicable |
| incomplete_reason | string or null | not_submitted: the attempt ended without being submitted (cancelled, left unfinished, or moved on by your team).One of: not_submitted, interview_not_finished, too_little_speech, no_answers, scoring_failed, recording_failed, not_delivered_in_time, other |
| score | integer or null | The AI score, 0–100. |
| recommendation | string or null | One of: strongly_recommend, recommended, consider, not_recommended |
| recommendation_label | string or null | |
| pass_mark | integer or null | The interview's pass mark, if it has one. |
| summary | string or null | |
| strengths | list of string | |
| areas_to_improve | list of string | |
| skills | map of integer | Skill name → score (0–100). |
| highlights | list of Highlight | |
| ended_for_conduct | boolean | The AI interview was ended for repeated conduct violations. |
Highlight
| Field | Type | What it is |
|---|---|---|
| quote | string | The candidate's own words. |
| type | string | One of: strength, concern, insight, conduct |
| why | string | One sentence on why it matters. |
AIInterview
| Field | Type | What it is |
|---|---|---|
| mode | string | One of: video, audio_only |
| language | string | |
| duration_seconds | integer or null | |
| questions_asked | integer | |
| questions_answered | integer | |
| recording | Recording | |
| transcript_url | string |
Recording
| Field | Type | What it is |
|---|---|---|
| status | string | processing: on its way (it can arrive up to about two days after the interview, even when the result is already final). failed: it didn't arrive or couldn't be processed. none: the candidate's device recorded nothing. deleted: removed after the retention period.One of: processing, ready, failed, none, deleted |
| url | string or null | API link (send your key) that returns a download link. Null when there's nothing to download. |
Answer
| Field | Type | What it is |
|---|---|---|
| question | string | |
| answer | string | Always text. Yes/no answers are "Yes" or "No". |
Document
| Field | Type | What it is |
|---|---|---|
| id | string | |
| label | string | What the interview asked for, e.g. "Your CV". |
| file_name | string | |
| content_type | string or null | |
| size_bytes | integer or null | |
| url | string | API link (send your key) that returns a download link. |
Interview
| Field | Type | What it is |
|---|---|---|
| id | string | |
| title | string | |
| status | string | One of: draft, active, paused, closed |
| language | string | |
| access_mode | string | One of: public, invite_only |
| pass_mark | integer or null | |
| candidate_link | string | The link candidates open. |
| created_at | time | |
| published_at | time or null | |
| closed_at | time or null | |
| steps | InterviewSteps |
InterviewSteps
| Field | Type | What it is |
|---|---|---|
| form | FormStep or null | |
| ai_interview | AIStep or null | |
| document | DocumentStep or null |
FormStep
| Field | Type | What it is |
|---|---|---|
| questions | list of FormQuestion |
FormQuestion
| Field | Type | What it is |
|---|---|---|
| id | string | |
| text | string | |
| type | string | One of: open_text, mcq, yes_no |
| required | boolean | |
| choices | list of string | Multiple-choice options; [] for other types. |
AIStep
| Field | Type | What it is |
|---|---|---|
| type | string | scripted: asks your questions word for word. ai_led: writes its own questions.One of: scripted, ai_led |
| mode | string | One of: video, audio_only |
| language | string | |
| time_limit_minutes | integer | |
| question_count | integer | |
| skills | list of string | The skills candidates are scored on. |
DocumentStep
| Field | Type | What it is |
|---|---|---|
| label | string | |
| description | string | |
| accepted_types | list of string | |
| required | boolean |
TranscriptEntry
| Field | Type | What it is |
|---|---|---|
| index | integer | |
| question | string | |
| answer | string | |
| speaking_seconds | integer |
RecordingDownload
| Field | Type | What it is |
|---|---|---|
| status | string | One of: processing, ready, failed, none, deleted |
| content_type | string | |
| duration_seconds | integer or null | |
| download_url | string | Works for 15 minutes, without your key. |
| expires_at | time |
FileDownload
| Field | Type | What it is |
|---|---|---|
| id | string | |
| name | string or null | |
| content_type | string or null | |
| size_bytes | integer or null | |
| download_url | string | Works for 15 minutes, without your key. |
| expires_at | time |
Event
One entry of the change history. submitted, scored and ready happen once per attempt; status_changed on every change after submission. Nothing is recorded before a candidate submits, or for previews.
| Field | Type | What it is |
|---|---|---|
| id | string | Stays the same if you receive the event again — use it to ignore repeats. |
| type | string | One of: candidate.submitted, candidate.scored, candidate.ready, candidate.status_changed |
| created_at | time | When it happened. |
| api_version | string | Always "v1" |
| test | boolean | true for samples sent from LessRounds; real events are false. |
| change | StatusChange or null | Only on candidate.status_changed. |
| candidate | Candidate | The candidate's latest data — not frozen at the time of the event. |
StatusChange
What a candidate.status_changed event changed, frozen when it happened.
| Field | Type | What it is |
|---|---|---|
| from | string | Public status code before the change. |
| to | string | Public status code after the change. |
| by | ChangedBy |
ChangedBy
| Field | Type | What it is |
|---|---|---|
| type | string | One of: team, ai, system, candidate |
| name | string | For team: the person's name. |
Me
| Field | Type | What it is |
|---|---|---|
| company | object | |
| company.id | string | |
| company.name | string | |
| api_key | object | |
| api_key.id | string | |
| api_key.name | string |
Error
| Field | Type | What it is |
|---|---|---|
| success | boolean | Always false |
| error | string | Error code: validation_error, not_found, auth_error, permission_denied, rate_limited, external_service_error, internal_error. |
| message | string | A plain-language explanation you can show to a person. |
| details | any | Extra detail for some errors, e.g. the list of problems with a query. |
| timestamp | integer | |
| trace_id | string | Quote this when you contact support. |