---
title: "Error Handling"
description: "Error shapes, status codes, and retry guidance."
collection: "api-reference"
slug: "errors"
url: "https://naiza.ai/docs/api-reference/errors"
markdown: "https://naiza.ai/docs/api-reference/errors.md"
full_docs: "https://naiza.ai/docs.md"
product: "Naiza"
base_url: "https://api.naiza.ai/api/v1"
---

# Error Handling

> Error shapes, status codes, and retry guidance.

## Table of contents

- [Error Response Format](#error-response-format)
- [HTTP Status Codes](#http-status-codes)
- [Error Codes](#error-codes)
  - [Authentication Errors](#authentication-errors)
  - [Validation Errors](#validation-errors)
  - [Not Found Errors](#not-found-errors)
  - [Rate Limiting Errors](#rate-limiting-errors)
  - [Conflict Errors](#conflict-errors)
  - [Server Errors](#server-errors)
- [Error Handling Best Practices](#error-handling-best-practices)
  - [1. Always Check Status Codes](#1-always-check-status-codes)
  - [2. Implement Retry Logic](#2-implement-retry-logic)
  - [3. Handle Rate Limits](#3-handle-rate-limits)
  - [4. Log Errors for Debugging](#4-log-errors-for-debugging)
- [Troubleshooting](#troubleshooting)
  - [Common Issues](#common-issues)
  - [Getting Help](#getting-help)

## Error Response Format

All errors follow a consistent JSON structure:

```json
{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error message",
    "details": {
      "field": "Additional context"
    }
  }
}
```

## HTTP Status Codes

| Status Code | Meaning | Description |
|------------|---------|-------------|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Invalid request parameters or body |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource not found |
| 409 | Conflict | Resource conflict (varies by endpoint) |
| 422 | Unprocessable Entity | Validation errors |
| 429 | Too Many Requests | Rate limit or quota exceeded |
| 500 | Internal Server Error | Server error |
| 503 | Service Unavailable | Service temporarily unavailable |

## Error Codes

### Authentication Errors

#### `UNAUTHORIZED` (401)
Invalid or missing API key.

```json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key",
    "details": {}
  }
}
```

**Resolution:**
- Verify the `x-api-key` header is present
- Check the API key format is correct
- Ensure the key hasn't been revoked

### Validation Errors

#### `VALIDATION_ERROR` (400)
Request validation failed.

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": {
      "eventName": "eventName is required",
      "customer.email": "Invalid email format"
    }
  }
}
```

**Resolution:**
- Review the `details` object for specific field errors
- Ensure all required fields are present
- Verify field formats match the API specification

#### `UNPROCESSABLE_ENTITY` (422)
Used when payload is structurally valid JSON but fails domain validation.

Common examples:

- `POST /api/v1/websdk/signals` with `sentAt` outside the allowed +/-5 minute window
- `POST /api/v1/websdk/signals` where `sdkSessionId` does not match the authenticated SDK token

### Not Found Errors

#### `NOT_FOUND` (404)
Requested resource doesn't exist.

```json
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Decision with ID evt_123 not found",
    "details": {}
  }
}
```

**Resolution:**
- Verify the resource ID is correct
- Check the resource belongs to your tenant
- Ensure the resource hasn't been deleted

### Rate Limiting Errors

#### `QUOTA_EXCEEDED` (429)
Quota limit reached.

```json
{
  "error": {
    "code": "QUOTA_EXCEEDED",
    "message": "Quota exceeded",
    "details": {
      "limit": 10000,
      "window": "1 hour",
      "retryAfter": 3600
    }
  }
}
```

**Resolution:**
- Wait for the quota window to reset
- Upgrade your plan for higher limits
- Implement request throttling in your application

See [Rate Limiting](https://naiza.ai/docs/api-reference/rate-limiting.md) for details.

### Conflict Errors

#### `CONFLICT` (409)
Resource conflict. Some endpoints may return `409` when a write cannot be applied due to a conflicting state.

```json
{
  "error": {
    "code": "CONFLICT",
    "message": "Conflict",
    "details": {
      "reason": "example"
    }
  }
}
```

**Resolution:**
- Inspect `details` for the specific conflict reason
- Adjust the request or resolve the conflicting state, then retry

### Server Errors

#### `INTERNAL_SERVER_ERROR` (500)
Unexpected server error.

```json
{
  "error": {
    "code": "INTERNAL_SERVER_ERROR",
    "message": "An unexpected error occurred",
    "details": {
      "requestId": "req_abc123"
    }
  }
}
```

**Resolution:**
- Retry the request after a short delay
- If the error persists, contact support with the `requestId`

#### `SERVICE_UNAVAILABLE` (503)
Service temporarily unavailable.

```json
{
  "error": {
    "code": "SERVICE_UNAVAILABLE",
    "message": "Service temporarily unavailable",
    "details": {
      "retryAfter": 60
    }
  }
}
```

**Resolution:**
- Wait for the specified `retryAfter` seconds
- Implement exponential backoff retry logic

## Error Handling Best Practices

### 1. Always Check Status Codes

```javascript
const response = await fetch(url, options);
if (!response.ok) {
  const error = await response.json();
  // Handle error based on error.code
}
```

### 2. Implement Retry Logic

```javascript
async function makeRequestWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);
      if (response.ok) return response;
      
      const errorData = await response.json();
      
      // Don't retry client errors (4xx) - they won't succeed on retry
      if (response.status >= 400 && response.status < 500) {
        const error = new Error(errorData.message || 'Client error');
        error.status = response.status;
        error.data = errorData;
        throw error;
      }
      
      // Retry server errors (5xx) with exponential backoff
      if (response.status >= 500) {
        await sleep(Math.pow(2, i) * 1000);
        continue;
      }
    } catch (error) {
      // Don't retry client errors (4xx) - they won't succeed on retry
      if (error.status >= 400 && error.status < 500) {
        throw error;
      }
      
      // Only retry network errors (no status) and server errors (5xx)
      // Network errors don't have a status property, so they'll be retried
      if (i === maxRetries - 1) throw error;
      await sleep(Math.pow(2, i) * 1000);
    }
  }
}
```

### 3. Handle Rate Limits

```javascript
async function handleRateLimit(response) {
  if (response.status === 429) {
    const error = await response.json();
    const retryAfter = error.error.details.retryAfter || 60;
    
    console.log(`Rate limited. Retrying after ${retryAfter} seconds`);
    await sleep(retryAfter * 1000);
    return true; // Indicate retry should happen
  }
  return false;
}
```

### 4. Log Errors for Debugging

```javascript
async function logError(error, context) {
  console.error('API Error:', {
    code: error.error.code,
    message: error.error.message,
    details: error.error.details,
    context,
    timestamp: new Date().toISOString(),
  });
}
```

## Troubleshooting

### Common Issues

#### "Invalid API key"
- Verify the key format: `naiza_api_sk_live_<32-chars>`
- Check for extra spaces or newlines
- Ensure the key hasn't been revoked

#### "Validation failed"
- Review the API documentation for required fields
- Check field types and formats
- Ensure JSON is properly formatted

#### "sdkSessionId does not match token"
- Ensure your backend mints the token and passes the same `sdkSessionId` to the browser
- Do not reuse a token across unrelated browser sessions

#### "Quota exceeded"
- Check your current usage in the dashboard
- Wait for the quota window to reset
- Consider upgrading your plan

#### "Service unavailable"
- Check Naiza status page
- Wait a few minutes and retry
- Contact support if the issue persists

### Getting Help

If you encounter errors not covered here:

1. Check the [API Reference](https://naiza.ai/docs/api-reference/overview.md) for endpoint-specific errors
2. Review your request format and parameters
3. Contact support with:
   - Error code and message
   - Request details (sanitized)
   - Request ID (if available)
   - Timestamp of the error

## Related documentation

- [API Overview](https://naiza.ai/docs/api-reference/overview.md) — Base URL, versioning, and high-level API surface.
- [Authentication](https://naiza.ai/docs/api-reference/authentication.md) — API keys, Web SDK tokens, and secure key handling.
- [Web SDK API](https://naiza.ai/docs/api-reference/websdk.md) — Browser SDK endpoints and device signal collection.
- [Events API](https://naiza.ai/docs/api-reference/events.md) — Submit and query product events for risk evaluation.
- [Sessions API](https://naiza.ai/docs/api-reference/sessions.md) — Session grouping and timeline endpoints.
- [Rate Limiting](https://naiza.ai/docs/api-reference/rate-limiting.md) — Quota headers and rate-limit behavior.
- [Decisions API](https://naiza.ai/docs/api-reference/decisions.md) — approve / deny / review evaluation and decision payloads (Events API uses ALLOW/REVIEW/BLOCK).
- [Lists API](https://naiza.ai/docs/api-reference/lists.md) — Allowlists, blocklists, and list membership management.

---

*Source: [https://naiza.ai/docs/api-reference/errors](https://naiza.ai/docs/api-reference/errors) · Full docs: [https://naiza.ai/docs.md](https://naiza.ai/docs.md)*
