Back to Home

REST API

Programmatic access to interviews. Schedule, retrieve, update, and delete interviews from your ATS or other internal tools.

Overview

All endpoints are versioned under /v1 and return JSON. Requests and responses use UTF-8. Timestamps are ISO 8601 strings in UTC.

Base URL

The API base URL is specific to your deployment. You can find it under Settings → Integrations. Examples in this document use {base_url} as a placeholder.

http
{base_url}/v1/interviews

Authentication

The API uses bearer tokens. Generate one under Settings → Integrations → API tokens and pass it in the Authorization header on every request. Treat tokens like passwords — the secret is shown only once.

http
Authorization: Bearer qlo_live_xxxxxxxxxxxxxxxxxxxxxxxx

Tokens act on behalf of the user who created them and inherit that user's billing balance and interview ownership. Revoking a token in the settings disables it immediately.

Errors

Errors use standard HTTP status codes with a JSON body containing a stable code and a human-readable message.

json
{
  "error": {
    "code": "validation_failed",
    "message": "Field \"title\" is required and must be a non-empty string"
  }
}
NameTypeDescription
400 validation_failed
client errorMalformed JSON, missing required field, or unknown field for the endpoint.
401 unauthenticated
client errorMissing or malformed Authorization header.
401 invalid_token
client errorToken does not exist or has been revoked.
402 insufficient_balance
client errorNot enough interview credits to schedule a new interview.
404 not_found
client errorInterview does not exist or is owned by a different user.
405 method_not_allowed
client errorHTTP method not supported for this path.
500 internal_error
server errorUnexpected server-side failure. Safe to retry with backoff.

Create interview

Schedules a new interview against an existing plan. Debits one interview credit from the caller's balance. The response contains the interview id and the 4-digit pin the candidate uses to join the room at /i/{id}.

POST/v1/interviews

Body

NameTypeDescription
titlerequired
stringDisplay title for the interview. Max 200 characters.
planIdrequired
stringID of the Plan to use as the interview script. Must belong to the caller.
intervieweeNamerequired
stringCandidate's name shown in the room and reports. Max 200 characters.

Example

bash
curl -X POST {base_url}/v1/interviews \
  -H "Authorization: Bearer $QUOLLO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Backend engineer — final round",
    "planId": "plan_01HZK...",
    "intervieweeName": "Jane Doe"
  }'
json
{
  "id": "9f1b6e8c-...",
  "pin": "4821",
  "balanceAfter": 12
}

List interviews

Returns interviews owned by the caller. Supports cursor-based pagination — pass nextCursor from the previous response back as the cursor query parameter to fetch the next page.

GET/v1/interviews

Query parameters

NameTypeDescription
limit
integerPage size. Default 20, max 100.
status
stringFilter by status. One of PENDING, IN_PROGRESS, FINISHED.
cursor
stringOpaque pagination cursor returned as nextCursor in the previous response.

Example

bash
curl "{base_url}/v1/interviews?status=FINISHED&limit=50" \
  -H "Authorization: Bearer $QUOLLO_TOKEN"
json
{
  "items": [
    {
      "id": "9f1b6e8c-...",
      "title": "Backend engineer — final round",
      "status": "FINISHED",
      "planId": "plan_01HZK...",
      "pin": "4821",
      "intervieweeName": "Jane Doe",
      "startedAt": "2026-05-18T10:02:14.000Z",
      "finishedAt": "2026-05-18T10:48:51.000Z",
      "createdAt": "2026-05-15T08:30:00.000Z",
      "updatedAt": "2026-05-18T10:48:51.000Z"
    }
  ],
  "nextCursor": "eyJpZCI6eyJTIjoiOWYxYjZlOGMtLi4uIn19"
}

Get interview

Returns the interview and, once the interview has finished and post-processing has run, the associated record with results, summary, participants, and a pre-signed video URL that is valid for one hour.

GET/v1/interviews/{id}

Path parameters

NameTypeDescription
idrequired
stringInterview ID returned from create.

Response shape

For non-finished interviews the record field is null. For finished interviews record.status moves through IDLE → PROCESSING → READY; the videoUrl is only populated when status is READY.

json
{
  "interview": {
    "id": "9f1b6e8c-...",
    "title": "Backend engineer — final round",
    "status": "FINISHED",
    "planId": "plan_01HZK...",
    "pin": "4821",
    "intervieweeName": "Jane Doe",
    "startedAt": "2026-05-18T10:02:14.000Z",
    "finishedAt": "2026-05-18T10:48:51.000Z",
    "createdAt": "2026-05-15T08:30:00.000Z",
    "updatedAt": "2026-05-18T10:48:51.000Z"
  },
  "record": {
    "status": "READY",
    "summary": "Strong on systems design, weaker on...",
    "results": [
      { "sectionId": "...", "questionId": "...", "score": 4, "notes": "..." }
    ],
    "participants": [
      { "name": "Jane Doe", "role": "INTERVIEWEE" }
    ],
    "videoUrl": "https://...s3.amazonaws.com/records/.../9f1b6e8c-....mp4?..."
  }
}

Update interview

Partial update. Only the fields listed below are writable. Sending any other field returns 400 validation_failed.

PATCH/v1/interviews/{id}

Body

NameTypeDescription
title
stringNew title. Max 200 characters.
intervieweeName
stringNew candidate name. Max 200 characters.
planId
stringReassign the interview to a different plan.

At least one of the fields above must be present in the body.

Example

bash
curl -X PATCH {base_url}/v1/interviews/9f1b6e8c-... \
  -H "Authorization: Bearer $QUOLLO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Backend engineer — round 2" }'

Delete interview

Deletes the interview. If the interview was still PENDING and a credit was charged when it was created, the credit is refunded and refunded: true with the new balanceAfter is returned. Interviews that have already started or finished are deleted without a refund.

DELETE/v1/interviews/{id}

Response

json
{
  "deleted": true,
  "refunded": true,
  "balanceAfter": 13
}

List plans

Returns plans owned by the caller. Use the resulting id as the planId when creating an interview. Only summary fields are returned — sections and questions live in the dashboard.

GET/v1/plans

Query parameters

NameTypeDescription
limit
integerPage size. Default 20, max 100.
cursor
stringOpaque pagination cursor returned as nextCursor in the previous response.

Response shape

NameTypeDescription
id
stringPlan ID. Pass this as planId when creating an interview.
name
stringDisplay name.
tags
string[]Free-form tags assigned to the plan.
seniority
string | nullOne of JUNIOR, MIDDLE, SENIOR. Null when not set on the plan.
language
stringPlan language as a 2-letter code (EN, DE, FR, ES, IT, NL, PL, PT, RU, UA, and other EU languages).

Example

bash
curl "{base_url}/v1/plans?limit=50" \
  -H "Authorization: Bearer $QUOLLO_TOKEN"
json
{
  "items": [
    {
      "id": "plan_01HZK...",
      "name": "Backend engineer — interview",
      "tags": ["backend", "node", "aws"],
      "seniority": "SENIOR",
      "language": "EN"
    }
  ],
  "nextCursor": null
}