---
title: "Errors"
url: https://docs.light-ai.app/errors.md
canonical_url: https://docs.light-ai.app/errors
last_updated: 2026-10-13
summary: "Problem-details errors with a stable code, what each code means and when to retry."
api_status: beta
related:
  - https://docs.light-ai.app/rate-limits
  - https://docs.light-ai.app/idempotency
---

# Errors

> Problem-details errors with a stable code, what each code means and when to retry.

Errors are [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details (`application/problem+json`). Branch on `code`, never on `title` or `detail`. Every error links here with `doc_url`.

**422 response**

```json
{
  "type": "https://docs.light-ai.app/errors#validation_failed",
  "title": "Validation failed",
  "status": 422,
  "detail": "The request is invalid. See errors[] for each field.",
  "instance": "/v1/leads",
  "code": "validation_failed",
  "doc_url": "https://docs.light-ai.app/errors#validation_failed",
  "request_id": "req_8bW2kq0xT4mZp9LcQ1vR7sYd",
  "errors": [
    {
      "pointer": "/contact/email",
      "detail": "An email or a phone number is required."
    }
  ]
}
```

## Retrying

- Retry `429`, `500` and `503` with exponential backoff, honoring `Retry-After`. Reuse the same `Idempotency-Key` so a retry never duplicates a write.
- Do not retry other `4xx` answers unchanged: fix the request first.
- Quote `request_id` (also the `Request-Id` header) when you contact support.

## Codes

| Code | Status | Title |
| --- | --- | --- |
| [`bad_request`](#bad_request) | 400 | Bad request |
| [`test_mode_mismatch`](#test_mode_mismatch) | 400 | Test mode mismatch |
| [`test_mode_unsupported`](#test_mode_unsupported) | 400 | Not available in test mode |
| [`unsupported_api_version`](#unsupported_api_version) | 400 | Unsupported API version |
| [`api_key_expired`](#api_key_expired) | 401 | API key expired |
| [`api_key_revoked`](#api_key_revoked) | 401 | API key revoked |
| [`api_key_suspended`](#api_key_suspended) | 401 | API key suspended |
| [`authentication_required`](#authentication_required) | 401 | Authentication required |
| [`invalid_api_key`](#invalid_api_key) | 401 | Invalid API key |
| [`insufficient_credits`](#insufficient_credits) | 402 | Insufficient credits |
| [`partner_spend_cap_reached`](#partner_spend_cap_reached) | 402 | Partner spend cap reached |
| [`api_access_not_enabled`](#api_access_not_enabled) | 403 | API access not enabled |
| [`api_disabled`](#api_disabled) | 403 | API disabled for this organization |
| [`forbidden`](#forbidden) | 403 | Forbidden |
| [`insufficient_scope`](#insufficient_scope) | 403 | Insufficient scope |
| [`ip_not_allowed`](#ip_not_allowed) | 403 | IP address not allowed |
| [`organization_off_season`](#organization_off_season) | 403 | Organization on Off-Season |
| [`organization_paused`](#organization_paused) | 403 | Organization paused |
| [`plan_upgrade_required`](#plan_upgrade_required) | 403 | Plan upgrade required |
| [`not_found`](#not_found) | 404 | Not found |
| [`method_not_allowed`](#method_not_allowed) | 405 | Method not allowed |
| [`billing_review_required`](#billing_review_required) | 409 | Billing review in progress |
| [`conflict`](#conflict) | 409 | Conflict |
| [`idempotency_in_progress`](#idempotency_in_progress) | 409 | Idempotent request in progress |
| [`idempotency_scope_mismatch`](#idempotency_scope_mismatch) | 409 | Idempotency scope mismatch |
| [`limit_reached`](#limit_reached) | 409 | Limit reached |
| [`quote_already_decided`](#quote_already_decided) | 409 | Quote already decided |
| [`quote_not_current`](#quote_not_current) | 409 | Quote is not current |
| [`stale_state`](#stale_state) | 409 | Stale state |
| [`payload_too_large`](#payload_too_large) | 413 | Payload too large |
| [`unsupported_media_type`](#unsupported_media_type) | 415 | Unsupported media type |
| [`idempotency_key_reused`](#idempotency_key_reused) | 422 | Idempotency key reused |
| [`validation_failed`](#validation_failed) | 422 | Validation failed |
| [`step_up_required`](#step_up_required) | 428 | Recent sign-in required |
| [`daily_cap_reached`](#daily_cap_reached) | 429 | Daily cap reached |
| [`rate_limited`](#rate_limited) | 429 | Rate limited |
| [`internal_error`](#internal_error) | 500 | Internal error |
| [`service_unavailable`](#service_unavailable) | 503 | Service unavailable |

### `bad_request`

**400 Bad request.** The request is malformed (JSON, headers or query). Read `detail`.

### `test_mode_mismatch`

**400 Test mode mismatch.** A test key used a live object, or the other way around.

### `test_mode_unsupported`

**400 Not available in test mode.** This endpoint works with live keys only.

### `unsupported_api_version`

**400 Unsupported API version.** The `Light-AI-Version` header names an unknown version. Use `2026-11-01`.

### `api_key_expired`

**401 API key expired.** Create or roll a key. Subscribe to `api_key.expiring` to hear about it 14 and 3 days ahead.

### `api_key_revoked`

**401 API key revoked.** The key was revoked or rolled past its grace period. Use the current key.

### `api_key_suspended`

**401 API key suspended.** The organization dropped below Pro (the key works again after an upgrade), or the key was suspended because its creator left or lost the role, or by a security reset (an owner re-affirms it in Settings → Developers).

### `authentication_required`

**401 Authentication required.** Send `Authorization: Bearer <key>`.

### `invalid_api_key`

**401 Invalid API key.** The key is malformed or unknown. Check for truncation or whitespace.

### `insufficient_credits`

**402 Insufficient credits.** Not enough credits for a mockup generation. `required` and `top_up_url` say how many and where to buy (organization keys also see `balance`). Nothing was charged.

### `partner_spend_cap_reached`

**402 Partner spend cap reached.** A partner hit the mockup spend cap the contractor set. The contractor can raise or remove it.

### `api_access_not_enabled`

**403 API access not enabled.** API access is not turned on for the organization (or your partner organization). E-mail support@light-ai.app.

### `api_disabled`

**403 API disabled for this organization.** Light AI turned API access off for this organization (or your partner organization). E-mail support@light-ai.app.

### `forbidden`

**403 Forbidden.** The credential cannot do this here (for example a grant without access to that object).

### `insufficient_scope`

**403 Insufficient scope.** The key (or grant) lacks the scope named in `detail`. Create a key with that scope or ask the contractor to re-consent.

### `ip_not_allowed`

**403 IP address not allowed.** The key has an IP allowlist that does not include this address.

### `organization_off_season`

**403 Organization on Off-Season.** The organization is on Off-Season: new leads (`POST /v1/leads`, inbound sources) are still accepted, every other write answers this until the owner resumes the plan in Light AI.

### `organization_paused`

**403 Organization paused.** The organization is paused: it is read-only and inbound leads are held until it resumes.

### `plan_upgrade_required`

**403 Plan upgrade required.** API access is included on Pro. The organization must upgrade (Free and Plus have no API access).

### `not_found`

**404 Not found.** No such object for this credential (it may belong to another organization or mode).

### `method_not_allowed`

**405 Method not allowed.** This path does not support the method. The `Allow` header lists those it does.

### `billing_review_required`

**409 Billing review in progress.** A billing review is in progress for the organization. Retry later or contact support.

### `conflict`

**409 Conflict.** The request conflicts with the current state. Read `detail`, refetch and retry.

### `idempotency_in_progress`

**409 Idempotent request in progress.** A request with the same `Idempotency-Key` is still running. Retry after a short wait.

### `idempotency_scope_mismatch`

**409 Idempotency scope mismatch.** The key was used for another organization or credential. Use a new key.

### `limit_reached`

**409 Limit reached.** A standing count limit was reached (`limit` says which: `webhook_endpoints`, `photo_uploads` open at once, or `open_connect_links`). Retrying does not help: delete or use one first.

### `quote_already_decided`

**409 Quote already decided.** The homeowner already approved or declined this quote in Light AI. Read the quote and sync its status instead.

### `quote_not_current`

**409 Quote is not current.** A newer revision of this quote exists. Use the project's current quote.

### `stale_state`

**409 Stale state.** The object changed since you read it (or is in a state that forbids this). Refetch and retry.

### `payload_too_large`

**413 Payload too large.** The body is too large. Upload big photos with a photo upload slot.

### `unsupported_media_type`

**415 Unsupported media type.** Send `Content-Type: application/json`.

### `idempotency_key_reused`

**422 Idempotency key reused.** The same `Idempotency-Key` was used with a different body. Use a new key for a new request.

### `validation_failed`

**422 Validation failed.** `errors[]` lists each invalid field as a JSON pointer with a message. Values are never echoed.

### `step_up_required`

**428 Recent sign-in required.** This action needs a recent sign-in in the app (management actions only).

### `daily_cap_reached`

**429 Daily cap reached.** A daily lead limit was reached: the inbound source's daily limit (`limit_scope: source`) or the organization's daily lead cap (`limit_scope: organization`). `Retry-After` points to midnight UTC.

### `rate_limited`

**429 Rate limited.** Too many requests. Wait for `Retry-After` seconds. See [Rate limits](https://docs.light-ai.app/rate-limits).

### `internal_error`

**500 Internal error.** Something failed on our side. Retry with the same `Idempotency-Key`; quote the `request_id` to support if it persists.

### `service_unavailable`

**503 Service unavailable.** A temporary outage. Retry with backoff and the same `Idempotency-Key`.
