API Reference

RecruitScreen API

Submit an interview recording (plus role title), receive a scored assessment. Interactive OpenAPI/Swagger lives at https://api.theleadvisionai.com/docs.

Authentication

Create an API key in your dashboard and pass it as a bearer token. Keys can be scoped to a single product or all three. The raw key is shown once; we store only its SHA-256 hash.

Authorization: Bearer lv_sk_recruitscreen_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Submit a job

POST https://api.theleadvisionai.com/v1/recruitscreen/jobs — multipart form. Returns 202 Accepted immediately; inference is asynchronous.

curl -X POST https://api.theleadvisionai.com/v1/recruitscreen/jobs \
  -H "Authorization: Bearer $LV_KEY" \
  -F file=@call.mp3 \
  -F role_title="Backend Engineer" \
  -F duration_s=412        # optional; improves credit estimate

# 202 →
{
  "job_id": "job_9f2c81d4a0b34e7f9d21",
  "status": "queued",
  "credits_charged": 7,
  "credits_remaining": 93,
  "status_url": "/v1/jobs/job_9f2c81d4a0b34e7f9d21",
  "events_url": "/v1/jobs/job_9f2c81d4a0b34e7f9d21/events"
}

Limits: audio ≤ 100 MB. Inputs are deleted after processing.

Job lifecycle

Our GPU loads models on demand and shares them across products. Job states make that visible so your UI can show honest progress instead of a spinner:

queued                # waiting for the GPU executor
loading_weights       # model weights streaming from NVMe → VRAM (cold start, 2–15s;
                      # skipped entirely when the model is already warm)
processing_inference  # your input is on the GPU
completed | failed    # terminal — result or error is populated
GET https://api.theleadvisionai.com/v1/jobs/{job_id}
Authorization: Bearer $LV_KEY

# 200 → { "job_id": "...", "status": "completed", "result": { ... }, ... }

Stream states (SSE)

const es = new EventSource("https://api.theleadvisionai.com/v1/jobs/job_.../events");
es.addEventListener("status", (e) => {
  const { status, detail, terminal } = JSON.parse(e.data);
  render(status);          // queued → loading_weights → processing_inference → completed
  if (terminal) es.close();
});

The stream replays the full state history on connect, so late subscribers see every transition.

Result schema

Key fields for RecruitScreen:

{
  "duration_s": 1820.4,
  "transcript": [{ "start": 0.0, "end": 4.1, "speaker": "SPEAKER_00", "text": "..." }],
  "metrics": { "talk_ratio": {...}, "interruptions": 1 },
  "assessment": {
    "summary": "...", "recommendation": "advance",
    "scores": { "communication": {"score": 4, "rationale": "..."}, ... },
    "highlights": ["..."], "red_flags": [],
    "questions_for_next_round": ["..."],
    "submittal_note": "client-ready paragraph"
  }
}

Credits & the 402 paywall

1 credit per audio minute. Credits are charged when the job is accepted — never twice, and a rejected job charges nothing. When your balance is empty the API returns 402:

HTTP/1.1 402 Payment Required
{
  "detail": {
    "error": "insufficient_credits",
    "credits_needed": 7,
    "message": "Credit balance exhausted. Subscribe to continue.",
    "upgrade_url": "https://recruitscreen.theleadvisionai.com/pricing"
  }
}

Errors

401  missing/invalid/revoked API key
402  credit balance exhausted (see upgrade_url)
403  key is scoped to a different product
404  unknown vertical or job (also returned for jobs you don't own)
413  file too large / too many images
422  missing required field (e.g. 'file')
429  demo rate limit (anonymous demos only)
507-equivalent: job fails with detail "vram_unavailable" if GPU memory is
     saturated after retries — credits for failed jobs are not re-charged on retry

Model status (public)

See exactly what the GPU pool is doing — useful for preflighting cold starts:

GET https://api.theleadvisionai.com/v1/models/status
# → { "models": [{ "role": "asr", "name": "whisper-large-v3-int8",
#                  "state": "warm", "footprint_mib": 3200, ... }],
#     "queue_depths": {}, "degraded": false }

Need higher limits, webhooks, or a custom-trained model on your own data? ajwahir@theleadvisionai.com