> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wiseyield.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors & status codes

> Canonical error response shape and the full list of error codes returned by the WiseYield API.

WiseYield API errors share a consistent shape so integrations can handle them uniformly. The HTTP status code communicates the **class** of error; the `code` field gives the **machine-readable kind**; `message` is human-readable; `details` carries structured context when applicable.

## Canonical response shape

```json theme={null}
{
  "error": "Validation failed",
  "code": "VALIDATION_ERROR",
  "message": "totalArea must be greater than 0",
  "details": {
    "totalArea": ["Total area must be greater than 0"]
  }
}
```

| Field     | Always present?                  | Notes                                                                                |
| --------- | -------------------------------- | ------------------------------------------------------------------------------------ |
| `error`   | Yes                              | Short, human-readable category (`Unauthorized`, `Validation failed`, `Not found`, …) |
| `code`    | When the error has a stable kind | Machine-readable identifier — use this for `switch` statements                       |
| `message` | Frequently                       | Longer human-readable description                                                    |
| `details` | When structured context exists   | Per-field validation errors, available scopes, retry hints, etc.                     |

## Status code classes

| Status | Class              | When                                                                  |
| ------ | ------------------ | --------------------------------------------------------------------- |
| `400`  | Bad request        | Malformed JSON, validation failed, business-rule violation            |
| `401`  | Unauthorized       | Auth missing, malformed, expired, or revoked                          |
| `403`  | Forbidden          | Authenticated but lacking the required scope, role, or whitelisted IP |
| `404`  | Not found          | Resource doesn't exist or is soft-deleted                             |
| `405`  | Method not allowed | HTTP method not implemented for this route                            |
| `409`  | Conflict           | Uniqueness violation (e.g. duplicate farm name)                       |
| `422`  | Unprocessable      | Semantically valid request but cannot be fulfilled                    |
| `429`  | Too many requests  | Rate limit exceeded                                                   |
| `5xx`  | Server error       | Transient — safe to retry with backoff                                |

## Error code reference

### Authentication and authorization

| `code`                   | Status | Meaning                                                |
| ------------------------ | ------ | ------------------------------------------------------ |
| `MISSING_API_KEY`        | 401    | No `Authorization` header sent                         |
| `INVALID_AUTH_FORMAT`    | 401    | Header is not `Bearer <key>`                           |
| `INVALID_API_KEY_FORMAT` | 401    | Key doesn't match `wy_(live\|test)_[a-f0-9]{48}`       |
| `INVALID_API_KEY`        | 401    | Key not found or revoked                               |
| `INACTIVE_API_KEY`       | 401    | Key exists but is currently disabled                   |
| `EXPIRED_API_KEY`        | 401    | Key passed its `expiresAt` timestamp                   |
| `UNAUTHORIZED`           | 401    | Generic auth failure (session-auth surfaces)           |
| `IP_NOT_WHITELISTED`     | 403    | Request came from an IP not in the key's allowlist     |
| `INSUFFICIENT_SCOPE`     | 403    | Key is valid but missing the scope this route requires |
| `FORBIDDEN`              | 403    | Authenticated but not authorized for this resource     |
| `AUTH_ERROR`             | 500    | Auth subsystem failure — retry with backoff            |

### Resource and validation

| `code`             | Status | Meaning                                                        |
| ------------------ | ------ | -------------------------------------------------------------- |
| `NOT_FOUND`        | 404    | Resource doesn't exist or has been soft-deleted                |
| `VALIDATION_ERROR` | 400    | Request body or query failed schema validation — see `details` |
| `INVALID_ID`       | 400    | Path parameter (e.g. `:id`) is not a valid UUID                |
| `CONFLICT`         | 409    | Uniqueness or state-machine violation                          |
| `LIMIT_EXCEEDED`   | 400    | A per-user limit (e.g. 10 API keys) was hit                    |

### Rate limiting

| `code`                | Status | Meaning                                                                                                                              |
| --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `RATE_LIMIT_EXCEEDED` | 429    | Per-user sliding-window limit reached. Response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (ISO 8601) |

### Server

| `code`                | Status | Meaning                                                                                                    |
| --------------------- | ------ | ---------------------------------------------------------------------------------------------------------- |
| `INTERNAL_ERROR`      | 500    | Generic unexpected failure                                                                                 |
| `SERVICE_UNAVAILABLE` | 503    | Downstream dependency unavailable (e.g. AI provider, FX rate source) — fail-open behavior varies per route |

## Validation error details

For `400 VALIDATION_ERROR`, `details` is an object whose keys are field names and whose values are arrays of human-readable messages:

```json theme={null}
{
  "error": "Validation failed",
  "code": "VALIDATION_ERROR",
  "details": {
    "name": ["Name is required"],
    "totalArea": ["Total area must be greater than 0"],
    "country": ["Country is required"]
  }
}
```

Top-level keys correspond to request body fields. Nested fields use dot-notation (`boundaries.coordinates`).

## Insufficient-scope details

For `403 INSUFFICIENT_SCOPE`, `details` tells you exactly what's needed:

```json theme={null}
{
  "error": "Forbidden",
  "code": "INSUFFICIENT_SCOPE",
  "message": "Insufficient permissions. Required scope: farms:write",
  "details": {
    "requiredScope": "farms:write",
    "availableScopes": ["farms:read", "crops:read"]
  }
}
```

Recreate the key with the missing scope, or rotate to a key that has it.

## Retry strategy by status

| Status             | Retry? | How                                                                                                   |
| ------------------ | ------ | ----------------------------------------------------------------------------------------------------- |
| `4xx` (except 429) | No     | Fix the request — retrying the same payload yields the same error                                     |
| `429`              | Yes    | Wait until `X-RateLimit-Reset` (ISO 8601), then retry. See [Rate-limit handling](/guides/rate-limits) |
| `5xx`              | Yes    | Exponential backoff with jitter, max 3 attempts                                                       |

## See also

* [Error-handling guide](/guides/errors) — language-specific patterns for typed error handling
* [Rate-limit handling](/guides/rate-limits) — backoff implementations
* [Authentication](/authentication) — the full key + scope contract
