> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.vlenseg.com/llms.txt.
> For full documentation content, see https://docs.vlenseg.com/llms-full.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.vlenseg.com/_mcp/server.

# Errors

Every Vlens response uses the same envelope. Errors are signalled by the `error_code` field — **not** the HTTP status. A `200 OK` response with `error_code` set is still an error.

```json
{
  "data": null,
  "error_code": 1050,
  "error_message": "Image quality too low for OCR extraction",
  "error_descriptions": null
}
```

| Field                | Description                                    |
| -------------------- | ---------------------------------------------- |
| `error_code`         | Numeric error code. `null` on success.         |
| `error_message`      | Human-readable description.                    |
| `error_descriptions` | Additional structured details, when available. |

***

## Handling errors

Always check `error_code` before treating a response as successful:

```javascript
const res = await fetch(url, options);
const body = await res.json();

if (body.error_code !== null) {
  throw new VlensError(body.error_code, body.error_message);
}

return body.data;
```

```python
res = requests.post(url, headers=headers, json=payload)
body = res.json()

if body["error_code"] is not None:
    raise VlensError(body["error_code"], body["error_message"])

return body["data"]
```

***

## Error categories

Specific error codes are listed in `error_code` alongside a descriptive `error_message`. Errors fall into the following categories:

### Authentication

| Cause                                                  | Resolution                                                 |
| ------------------------------------------------------ | ---------------------------------------------------------- |
| `ApiKey` header missing or invalid                     | Verify the `ApiKey` is present and active in the dashboard |
| `Authorization` header missing on a protected endpoint | Add `Authorization: Bearer <token>`                        |
| Access token expired                                   | Refresh via `/api/credentials/RefreshToken`                |
| Refresh token expired or revoked                       | Re-authenticate via `/api/credentials/Login`               |
| Token does not have permission for this endpoint       | Use an admin token; check role assignments in the portal   |
| Token belongs to a different tenant                    | Use credentials for the correct tenant                     |

### Registration

| Cause                             | Resolution                                                     |
| --------------------------------- | -------------------------------------------------------------- |
| Phone or email already registered | Call `/api/DigitalIdentity/CheckExistenceOfEmailOrPhone` first |
| Invalid OTP                       | Request a new OTP after the user re-enters                     |
| OTP expired or request not found  | Restart the OTP flow from `StepVerifyPhone`                    |

### Identity verification

| Cause                                         | Resolution                                                                               |
| --------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Image quality too low (blurry, dark, distant) | Re-capture with better lighting and the ID filling most of the frame                     |
| Document detected as fake                     | Re-capture the **physical** ID — photocopies and screen captures fail spoofing           |
| Liveness failed                               | Re-capture three distinct frames within 0.5s — see [Digital Identity](/digital-identity) |
| Identical face images                         | Capture three distinct, sequential frames                                                |
| Transaction not found                         | Restart from `verify/id/front` to obtain a new transaction                               |
| NTRA or CSO authority check failed            | The ID could not be verified against national records — escalate via support             |

### Business request

| Cause                                   | Resolution                                                                             |
| --------------------------------------- | -------------------------------------------------------------------------------------- |
| User not eligible                       | Inspect `CheckEligibility` and fix the specific failure (KYC, email, pending request)  |
| Wrong request status for this operation | Confirm `requeststatus` via `GetBusinessRequestById` matches what the endpoint expects |
| Location changed — liveness required    | Call `StepReValidateLiveness` before retrying `StepRequestOtp`                         |
| Invalid OTP (signing)                   | Request a new OTP via `StepRequestOtp`                                                 |
| Required form field missing             | Inspect the field list from `GetBusinessRequestTypeTemplateFields`                     |

***

## Retry strategy

| Error class                    | Strategy                                                                   |
| ------------------------------ | -------------------------------------------------------------------------- |
| Auth (token expired)           | Refresh the token, then retry once                                         |
| Validation / business errors   | Do **not** retry — fix the request or surface to the user                  |
| Network errors / server errors | Retry with exponential backoff (e.g. 500ms → 1s → 2s → 4s, max 3 attempts) |

```javascript
async function callVlensWithRetry(fn, maxAttempts = 3) {
  let delay = 500;
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (err) {
      // Only retry transient errors — never retry validation or auth failures
      if (!isTransient(err) || attempt === maxAttempts) throw err;
      await new Promise(r => setTimeout(r, delay));
      delay = Math.min(delay * 2, 8000);
    }
  }
}
```

***

## Validation errors

When request fields fail validation, the response may include structured field-level details in `error_descriptions`:

```json
{
  "data": null,
  "error_code": 400,
  "error_message": "Validation failed",
  "error_descriptions": [
    { "field": "image", "message": "image is required" }
  ]
}
```

Iterate `error_descriptions` to surface field-level errors in your UI.

***

## Getting help

If you encounter an error not listed here, contact [support@vlenseg.com](mailto:support@vlenseg.com) with:

1. The endpoint URL and method
2. The full request body (redact images and PII)
3. The full response body including `error_code` and `error_message`
4. Your tenant name and approximate UTC timestamp