> For the complete documentation index, see [llms.txt](https://finext.gitbook.io/one-kyc/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://finext.gitbook.io/one-kyc/api/errors-and-limits.md).

# Errors and limits

Every Tenant API endpoint uses a single error format and standard HTTP status codes. The full list of endpoints and response examples is in the [API reference (OpenAPI)](/one-kyc/api/api-reference.md).

## Error format

Error responses are JSON with a single shape:

```json
{
  "success": false,
  "error": {
    "type": "invalid_request_error",
    "code": "validation_error",
    "message": "Request validation failed",
    "param": "flow_id",
    "details": [
      {
        "field": "flow_id",
        "code": "required",
        "message": "flow_id is required"
      }
    ],
    "request_id": "req_abc123",
    "retryable": false
  }
}
```

**Field reference:**

| Field              | Description                                                                                                                                                                                                                                     |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `success`          | Always `false` on error                                                                                                                                                                                                                         |
| `error.type`       | Error category, derived from the HTTP status: `authentication_error` (401), `authorization_error` (403), `not_found_error` (404), `rate_limit_error` (429), `service_unavailable` (503), `api_error` (5xx), `invalid_request_error` (other 4xx) |
| `error.code`       | Error code (for example, `validation_error`, `not_found`, `unauthorized`, `conflict`)                                                                                                                                                           |
| `error.message`    | Short error description (for developers and logging)                                                                                                                                                                                            |
| `error.param`      | Parameter that caused the error (for validation errors)                                                                                                                                                                                         |
| `error.details`    | Per-field error array (for validation errors)                                                                                                                                                                                                   |
| `error.request_id` | Request identifier (for support enquiries)                                                                                                                                                                                                      |
| `error.doc_url`    | Link to the relevant documentation section (when available)                                                                                                                                                                                     |
| `error.retryable`  | Whether the request is safe to retry (`true` for some 5xx errors and timeouts)                                                                                                                                                                  |

## HTTP status codes

| Code    | Description                                                                                 |
| ------- | ------------------------------------------------------------------------------------------- |
| **200** | Request succeeded                                                                           |
| **201** | Resource created                                                                            |
| **400** | Bad request (error in body or parameters)                                                   |
| **401** | Authentication failed (HMAC signature / JWT token missing or invalid)                       |
| **403** | Forbidden (insufficient scope or attempt to access another tenant's resource)               |
| **404** | Resource not found (session, applicant, link, etc.)                                         |
| **409** | Conflict (for example, the review task is already assigned or the decision is already made) |
| **410** | Resource no longer available (for example, session expired)                                 |
| **422** | Field validation failed                                                                     |
| **429** | Rate limit exceeded; the **Retry-After** header indicates how long to wait                  |
| **500** | Internal server error; if `error.retryable = true`, the request can be retried              |

## Rate limiting

Limits apply per IP address and per API key. Default values:

| Scope          | Requests/sec | Requests/min |
| -------------- | ------------ | ------------ |
| Per IP address | 20           | 200          |
| Per API key    | 40           | 400          |

When the limit is exceeded, the server returns HTTP 429 Too Many Requests. Response headers:

| Header                  | Description                            |
| ----------------------- | -------------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests in the current window |
| `X-RateLimit-Remaining` | Remaining requests                     |
| `X-RateLimit-Reset`     | Unix time at which the limit resets    |
| `Retry-After`           | Seconds until the next allowed attempt |

**Recommendation:** apply exponential backoff on retries and honour the `Retry-After` header.

## Common errors

### `invalid_signature` (401)

Invalid HMAC signature. Recheck the signature construction (see [Authentication](/one-kyc/api/authentication.md)).

### `invalid_signature` (401) — clock drift

If `X-Timestamp` deviates from the server time by more than 5 minutes, the server also returns `invalid_signature` (there is no separate `timestamp_expired` code). Confirm that the client clock is synchronised (NTP).

### `unauthorized` (403)

The API key does not have sufficient scopes. Review the key scopes in the admin panel.

### `not_found` (404)

The requested resource (applicant, session, link, etc.) does not exist or belongs to another tenant.

### `link_already_consumed` (409)

The verification link has already been consumed. Create a new link.

### `link_expired` (410)

The verification link expired. Create a new link with a longer `ttl_seconds`.

### `validation_error` (422)

Request fields failed validation. Per-field details are in the `error.details` array.

### `rate_limit_exceeded` (429)

Rate limit exceeded. Honour the `Retry-After` header and apply exponential backoff.

### `internal_server_error` (500)

Internal server error. Check `error.retryable` — when `true`, the request can be retried.

## OTP verification: limits and errors

OTP confirms a phone number or email address during a KYC session.

### OTP code parameters

| Parameter               | Value                                                                                                                                     |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Code length             | 6 digits                                                                                                                                  |
| Code lifetime           | Phone: 10 minutes by default. Email: 15 minutes by default. Flow step configuration can override the lifetime within the supported range. |
| Minimum resend interval | 15 seconds by default; flow step configuration can override it                                                                            |
| Maximum entry attempts  | 10                                                                                                                                        |

### OTP errors

On failed verification, OTP endpoints return the standard error envelope with an OTP error code and a text message explaining the reason. When available, the remaining attempt count is included in `error.details` as `attempts_remaining`.

#### `rate_limit_exceeded` (429)

OTP send rate limit exceeded. Wait until the limit resets (see the `Retry-After` header).

#### `otp_invalid` / `otp_expired` (400/422)

The code expired (older than 10 minutes). Request a new code through `POST .../otp/send`.

#### `otp_invalid` (400/422)

The entered code is wrong. The remaining attempt count is in `error.details` when the endpoint returns it.

#### `otp_invalid` (400/422), max attempts exceeded

All 10 attempts were used. The code is invalidated — request a new one through `POST .../otp/send`.

***

**See also:** [Authentication](/one-kyc/api/authentication.md) (codes 401, 403), [API reference (OpenAPI)](/one-kyc/api/api-reference.md) (full endpoint and response code specification).
