Programmatically send invites and fetch submission results from your own backend. Bearer-API-key auth, org-scoped.

REST API

api/v1/* is Litmus's public REST surface. Use it from a backend (ATS, your own portal, a coding-contest workflow) to send candidate invites and pull submission results without going through the dashboard. Every request is scoped to the calling org via a Bearer API key minted in Settings → API Keys.

The same key works for the MCP server; REST and MCP are two ways to call the same underlying handlers.

Quickstart

1. Mint an API key

Dashboard → Settings → API Keys → New key. You'll see the full key once; copy it.

2. List your roles

bash
curl https://www.litmushiring.com/api/v1/roles \
  -H "Authorization: Bearer litmus_sk_..."

Returns:

json
{
  "roles": [
    {
      "id": "role_abc123",
      "title": "Backend Engineer",
      "status": "open",
      "team": "Platform",
      "location": "Remote",
      "type": "full_time",
      "assessment_count": 2,
      "created_at": "2026-05-22T..."
    }
  ]
}

3. Send an invite

bash
curl -X POST https://www.litmushiring.com/api/v1/invites \
  -H "Authorization: Bearer litmus_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "posting_id": "role_abc123",
    "candidate_email": "candidate@example.com",
    "candidate_name": "Sample Candidate"
  }'

Returns:

json
{
  "invite_id": "cand_xyz789",
  "invite_url": "https://www.litmushiring.com/sign-in?assessmentToken=..."
}

Litmus emails the candidate automatically. The invite_url is the same link the email contains; surface it in your own portal if you want.

posting_id accepts both roles and cohorts (a cohort is a bundle of assessments). Pass the id of either — the request is identical:

bash
curl -X POST https://www.litmushiring.com/api/v1/invites \
  -H "Authorization: Bearer litmus_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "posting_id": "cohort_neo2026",
    "candidate_email": "candidate@example.com",
    "candidate_name": "Sample Candidate"
  }'

Find a posting's id in its dashboard URL (/company/posting/<id>). GET /api/v1/roles lists role ids; cohort ids aren't listed there yet.

assessment_id is optional — when omitted, the posting's first pipeline assessment is used. Pass one explicitly to target a later stage.

role_id is deprecated. The endpoint originally accepted role_id; it still works, but only for role-type postings, and will be removed in a future version. Use posting_id instead. Passing a cohort id via role_id returns 400 ROLE_ID_TYPE_MISMATCH.

Invite errors

StatuserrorMeaning
404posting_id not foundUnknown id, or it belongs to another org.
400ROLE_ID_TYPE_MISMATCHA non-role id was sent via role_id — use posting_id.
400SELF_SELECT_UNSUPPORTEDThe assessment is in a self-select group — not yet invitable via the API.
400POSTING_CLOSEDReopen the posting before inviting.
409ALREADY_INVITEDThe candidate is already on that assessment.

4. Poll for results

bash
curl https://www.litmushiring.com/api/v1/submissions/<submission_id> \
  -H "Authorization: Bearer litmus_sk_..."

Returns:

json
{
  "id": "sub_def456",
  "assessment_id": "asmt_ghi789",
  "candidate": { "email": "candidate@example.com", "name": "Sample Candidate" },
  "score": 4.2,
  "decision": "advance",
  "submitted_at": "2026-05-26T...",
  "report_url": "https://www.litmushiring.com/company/candidates?submission=sub_def456"
}

score is null until grading completes (typically a few minutes after the candidate submits). decision defaults to "pending" and flips when a reviewer makes a call in the dashboard.

Endpoints

MethodPathPurpose
GET/api/v1/rolesList all roles in your org.
POST/api/v1/invitesSend an invite to one candidate.
GET/api/v1/submissions/{id}Read a single submission.

Authentication

Every request needs an Authorization: Bearer litmus_sk_... header. Keys are minted in Settings → API Keys and live for as long as the page row exists; revoke from the same screen.

  • 401 means the header is missing, malformed, names an unknown prefix, or the key was revoked. The error message is intentionally the same in every case ("Invalid API key") so probes can't fingerprint the failure mode.
  • One key can have any number of in-flight requests. There's no per-key rate limit today — but please don't hammer; we'll add caps if abuse shows up.

Webhooks?

Not yet. Polling GET /api/v1/submissions/{id} is fine for low-volume flows (Neo's launch handles 5–10 candidates/day comfortably). When a specific integration blocks on push delivery, we'll build webhooks with proper retry/idempotency/signing — until then, half-built push delivery would be worse than none.

Versioning

v1 is a frozen contract. New fields are additive; existing fields don't change shape. Breaking changes ship as /api/v2/* with both versions running in parallel.