Skip to main content

Errors

The envelope, and what to branch on.

Every error is the same shape.

{
  "error": {
    "type": "invalid_request_error",
    "code": "validation_failed",
    "message": "duration_minutes must be between 5 and 120",
    "param": "duration_minutes",
    "request_id": "req_8f2c...",
    "doc_url": "https://docs.experthire.io/..."
  }
}

Branch on code, never on message. Messages are written for people and change; codes are a closed set.

Quote request_id when you contact us. It is on every response, including successes.

The ones you will actually hit

CodeStatusWhat to do
invalid_api_key401Check the prefix. An ehp_ key is the wrong product
secret_key_required401You sent a session token to a server-only endpoint
validation_failed400Read param
insufficient_credits402Stop and tell the customer
sandbox_quota_exceeded402The month's sandbox allowance is used up
assessment_not_found404Wrong id, wrong organisation, or wrong environment
report_not_ready404Not completed yet. Sandbox returns a sample instead
rate_limited429Back off for Retry-After
idempotency_key_reuse409Same key, different body

Everything else

CodeStatusWhat to do
missing_api_key401No Authorization header
session_expired401The browser session lapsed or was revoked. Mint a new one
launch_token_expired401The link outlived its window. Mint a new launch link
launch_token_used401Single use. Mint a new one; do not retry the same link
api_not_enabled403The Public API is off for this organisation. Contact us
module_disabled403That assessment type is not enabled. Nothing was charged
live_key_required403The endpoint reads or writes production data with no sandbox equivalent
origin_not_allowed403Allowlist it with POST /v1/domains first
key_not_found404Wrong key id, or it belongs to the other environment
resume_ingest_in_flight409A resume is already processing. Poll, do not re-upload
judging_in_progress409Submissions are still being scored. Retry finalize
invalid_request400The body could not be parsed at all
invalid_cursor400Cursors are opaque. Pass back what we gave you, unmodified
invalid_origin400Send scheme and host only, e.g. https://app.example.com
invalid_environment400Unknown environment name
assessment_started400Too late to change it. Cancel instead
candidate_not_editable400Shared with another organisation, or that email is taken
key_revokes_self400Send a grace window, or rotate first and revoke after
language_not_allowed400Read GET /v1/coding-languages and the test's allowed_languages
unsupported_operation400That call does not apply to this assessment type
unprocessable_resumen/aNot an HTTP status. It arrives as error_code on GET /v1/assessments/{id}/resume

Three of these are worth reading twice.

live_key_required is not a bug to work around. Branding, members and analytics are organisation-level with no sandbox equivalent, so a test key is refused rather than shown production data.

resume_ingest_in_flight means the upload succeeded and scoring is running. Re-uploading starts nothing; poll GET /v1/assessments/{id}/resume until it reads scored or failed.

judging_in_progress is the retry signal on finalize, not a failure. The assessment is still resumable until it returns 200, so do not mark it complete on the 409.

Not-found means several things

An id from another organisation, or from the other environment, returns assessment_not_found rather than a permission error. That is deliberate: a distinct error would let an id be probed for existence across tenants.

Retrying safely

Send Idempotency-Key on every create. A retry then replays the original response instead of creating a second assessment and billing twice.

A failed request releases its key, so a retry after a 500 does the work rather than replaying the failure.

Next

  • Reference: rate limits, pagination, idempotency