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

# Errors

> Understand Operational Evidence HTTP errors, public error codes, request IDs, and retry behavior.

# Errors

The Wo returns errors using a consistent public JSON envelope.

## Standard error shape

```json theme={null}
{
  "error": {
    "code": "invalid_request_payload",
    "message": "Request body does not match the create evidence report contract.",
    "requestId": "req_123"
  }
}
```

`requestId` identifies the HTTP request.

Keep it when reporting an integration problem.

## Error details

Some errors may include safe structured metadata:

```json theme={null}
{
  "error": {
    "code": "quota_exceeded",
    "message": "Monthly operation quota has been exceeded.",
    "requestId": "req_123",
    "details": {
      "operation": "evidence_report",
      "limit": 500,
      "used": 500,
      "remaining": 0,
      "requestedUnits": 1,
      "resetAt": "2026-09-01T00:00:00.000Z"
    }
  }
}
```

The API does not expose stack traces, credentials, database errors, provider
credentials, or other private implementation details through this envelope.

## Public errors

| HTTP | Code                                 | Meaning                                                                  |
| ---: | ------------------------------------ | ------------------------------------------------------------------------ |
|  400 | `invalid_json`                       | Request body contains malformed JSON.                                    |
|  400 | `invalid_request_payload`            | JSON does not match the public endpoint contract.                        |
|  400 | `invalid_idempotency_key`            | `Idempotency-Key` is blank, repeated, or exceeds the supported length.   |
|  401 | `missing_api_key`                    | `x-api-key` was not provided.                                            |
|  401 | `invalid_api_key`                    | Credential is invalid, inactive, or revoked.                             |
|  403 | `quota_access_denied`                | Customer does not currently have access to the billable operation.       |
|  403 | `current_usage_access_denied`        | Current usage is not available for the authenticated customer.           |
|  403 | `product_not_entitled`               | Customer is not currently entitled to execute Operational Evidence.      |
|  409 | `idempotency_conflict`               | The same idempotency key was already used for a different request.       |
|  409 | `idempotency_in_progress`            | The same logical request is currently being processed.                   |
|  422 | `invalid_evidence_report_input`      | Application input was rejected after HTTP validation.                    |
|  422 | `remote_evidence_image_rejected`     | A submitted remote image could not be accepted safely.                   |
|  429 | `quota_exceeded`                     | Monthly operation quota is exhausted.                                    |
|  429 | `rate_limit_exceeded`                | The caller exceeded the configured technical request rate.               |
|  503 | `entitlement_check_unavailable`      | Product entitlement cannot currently be evaluated safely.                |
|  503 | `rate_limit_unavailable`             | Rate-limit evaluation is temporarily unavailable.                        |
|  503 | `remote_evidence_image_unavailable`  | A remote image cannot currently be retrieved safely.                     |
|  502 | `invalid_generated_report`           | Generated output could not be represented by the public report contract. |
|  502 | `report_generation_failed`           | Operational report generation failed.                                    |
|  503 | `api_key_authentication_unavailable` | Authentication cannot currently be completed safely.                     |
|  503 | `quota_check_unavailable`            | Monthly quota validation is temporarily unavailable.                     |
|  503 | `report_generation_unavailable`      | Report generation is temporarily unavailable.                            |
|  503 | `evidence_report_write_failed`       | Generated report could not be stored.                                    |
|  503 | `usage_recording_failed`             | Report usage could not be recorded.                                      |
|  503 | `current_usage_unavailable`          | Current usage information cannot currently be read safely.               |
|  503 | `idempotency_unavailable`            | Idempotency protection is temporarily unavailable.                       |
|  503 | `idempotency_replay_unavailable`     | The original idempotent result cannot currently be restored safely.      |
|  500 | `internal_error`                     | An unexpected internal error occurred.                                   |

## 400 — Invalid payload

An HTTP-valid JSON document that does not match the endpoint schema returns:

```http theme={null}
HTTP/1.1 400 Bad Request
```

Example:

```json theme={null}
{
  "error": {
    "code": "invalid_request_payload",
    "message": "Request body does not match the create evidence report contract.",
    "requestId": "req_123"
  }
}
```

This includes unsupported `context` values and invalid public request shapes.

Malformed JSON is represented separately:

```json theme={null}
{
  "error": {
    "code": "invalid_json",
    "message": "Request body contains invalid JSON.",
    "requestId": "req_123"
  }
}
```

Correct the payload before retrying.

## 401 — Authentication

Missing API key:

```json theme={null}
{
  "error": {
    "code": "missing_api_key",
    "message": "The x-api-key header is required.",
    "requestId": "req_123"
  }
}
```

Invalid, inactive, or revoked API key:

```json theme={null}
{
  "error": {
    "code": "invalid_api_key",
    "message": "The provided API key is invalid.",
    "requestId": "req_123"
  }
}
```

Do not repeatedly retry an authentication error with the same invalid
credential.

## 403 — Billable operation unavailable

```json theme={null}
{
  "error": {
    "code": "quota_access_denied",
    "message": "The customer is not allowed to execute this billable operation.",
    "requestId": "req_123"
  }
}
```

This can represent an unavailable customer or active Plan relationship.

It is intentionally different from quota exhaustion.

## 403 — Current usage unavailable for customer

Current usage visibility uses a separate access error from billable operation
execution:

```json theme={null}
{
  "error": {
    "code": "current_usage_access_denied",
    "message": "Current usage is not available for this customer.",
    "requestId": "req_123"
  }
}
```

This can represent an unavailable active customer or Plan relationship needed
to expose the supported usage operation.

## 403 — Product not entitled

A valid API key identifies the customer, but it does not by itself grant access
to Operational Evidence. If the authenticated customer is not currently
entitled to execute the `evidence_report` operation, the API returns:

```json theme={null}
{
  "error": {
    "code": "product_not_entitled",
    "message": "The customer is not entitled to create Operational Evidence reports.",
    "requestId": "req_123"
  }
}
```

This is a product-access decision, not quota exhaustion. Do not repeatedly
retry the same request until product access changes. Internal entitlement
identifiers, reasons, billing state, and subscription metadata are not exposed.

## 409 — Idempotency conflict or operation in progress

Reusing the same `Idempotency-Key` for a different normalized request returns:

```json theme={null}
{
  "error": {
    "code": "idempotency_conflict",
    "message": "The idempotency key was already used with a different request.",
    "requestId": "req_123"
  }
}
```

A concurrent retry while the original request is still processing returns:

```json theme={null}
{
  "error": {
    "code": "idempotency_in_progress",
    "message": "An operation with this idempotency key is still in progress.",
    "requestId": "req_123"
  }
}
```

Do not retry `idempotency_conflict` with the same key and a different request.
For `idempotency_in_progress`, wait until the original request has finished
before deciding whether another retry is appropriate.

## 422 — Application input rejected

```json theme={null}
{
  "error": {
    "code": "invalid_evidence_report_input",
    "message": "Evidence report input is invalid.",
    "requestId": "req_123"
  }
}
```

This represents a request that reached the application boundary but could not
be represented as a valid Evidence Report operation.

## 422 — Remote evidence image rejected

When a submitted image URL cannot be accepted through the secure remote-resource
boundary, the API returns:

```json theme={null}
{
  "error": {
    "code": "remote_evidence_image_rejected",
    "message": "One or more remote evidence images could not be accepted safely.",
    "requestId": "req_123"
  }
}
```

This can represent a remote resource that violates the supported remote-image
policy, including unsafe destinations or redirects, oversized content, invalid
response metadata, or unsupported image content types.

The public error intentionally does not expose blocked addresses, private DNS
answers, redirect destinations, socket details, or raw network diagnostics.

Correct or replace the image resource before retrying.

## 429 — Quota exceeded

```json theme={null}
{
  "error": {
    "code": "quota_exceeded",
    "message": "Monthly operation quota has been exceeded.",
    "requestId": "req_123",
    "details": {
      "operation": "evidence_report",
      "limit": 500,
      "used": 500,
      "remaining": 0,
      "requestedUnits": 1,
      "resetAt": "2026-09-01T00:00:00.000Z"
    }
  }
}
```

Do not continuously retry this response.

Use `resetAt` to understand when the next monthly quota period begins.

## 429 — Rate limit exceeded

Rate limiting protects API capacity and is separate from monthly quota.

When the configured technical request rate is exceeded:

```json theme={null}
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Try again later.",
    "requestId": "req_123",
    "details": {
      "limit": 60,
      "remaining": 0,
      "resetAt": "2030-01-01T00:01:00.000Z",
      "retryAfterSeconds": 30
    }
  }
}
```

The numeric values above are illustrative.

The response also includes:

```http theme={null}
Retry-After: 30
```

Clients should wait for the indicated interval before retrying.

Do not interpret `rate_limit_exceeded` as commercial quota exhaustion.

## 503 — Entitlement check unavailable

If The Wo cannot safely evaluate Operational Evidence product entitlement, the
create operation fails closed:

```json theme={null}
{
  "error": {
    "code": "entitlement_check_unavailable",
    "message": "Product entitlement validation is temporarily unavailable.",
    "requestId": "req_123"
  }
}
```

A bounded retry may be appropriate because this represents temporary entitlement
evaluation failure rather than a definitive product-access decision. Preserve
`requestId` when reporting a persistent problem.

## 503 — Rate limiting unavailable

If the platform cannot safely evaluate the configured technical rate limit, it returns:

```json theme={null}
{
  "error": {
    "code": "rate_limit_unavailable",
    "message": "Rate limiting is temporarily unavailable.",
    "requestId": "req_123"
  }
}
```

Internal limiter or storage diagnostics are never exposed.

## 502 — Generation failure

An invalid generated report:

```json theme={null}
{
  "error": {
    "code": "invalid_generated_report",
    "message": "The report provider returned an invalid response.",
    "requestId": "req_123"
  }
}
```

A generation failure:

```json theme={null}
{
  "error": {
    "code": "report_generation_failed",
    "message": "Operational report generation failed.",
    "requestId": "req_123"
  }
}
```

These responses do not expose raw provider errors.

## 503 — Idempotency unavailable

If idempotency protection cannot be established safely, the API returns:

```json theme={null}
{
  "error": {
    "code": "idempotency_unavailable",
    "message": "Idempotency protection is temporarily unavailable.",
    "requestId": "req_123"
  }
}
```

If a completed idempotent operation exists but its original Evidence Report
cannot be restored safely, the API returns:

```json theme={null}
{
  "error": {
    "code": "idempotency_replay_unavailable",
    "message": "The original idempotent result could not be restored safely.",
    "requestId": "req_123"
  }
}
```

These responses do not expose request fingerprints, MongoDB records, resource
ownership information, provider details, or other internal diagnostics.

## 503 — Current usage unavailable

If the API cannot safely read current usage information, it returns:

```json theme={null}
{
  "error": {
    "code": "current_usage_unavailable",
    "message": "Current usage information is temporarily unavailable.",
    "requestId": "req_123"
  }
}
```

The API does not replace a failed usage read with `used: 0`.

Retry cautiously and preserve the `requestId` if the problem persists.

## 503 — Remote evidence image unavailable

When a remote image cannot currently be retrieved safely because the remote
resource or retrieval dependency is temporarily unavailable, the API returns:

```json theme={null}
{
  "error": {
    "code": "remote_evidence_image_unavailable",
    "message": "One or more remote evidence images could not be retrieved safely.",
    "requestId": "req_123"
  }
}
```

A bounded retry may be appropriate. Preserve `requestId` when reporting a
persistent failure.

## 503 — Provider unavailable

A temporarily unavailable report generation service returns:

```json theme={null}
{
  "error": {
    "code": "report_generation_unavailable",
    "message": "Operational report generation is temporarily unavailable.",
    "requestId": "req_123"
  }
}
```

A `503` may also represent another required service being unavailable.

Always inspect `error.code`; do not branch only on the HTTP status.

## 500 — Unexpected error

Unexpected failures use:

```json theme={null}
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected error occurred.",
    "requestId": "req_123"
  }
}
```

Internal implementation details are not returned.

Preserve the `requestId` when reporting the problem.

## Retry guidance

Do not automatically retry every failed request.

A practical policy is:

| Response | Retry behavior                                       |
| -------- | ---------------------------------------------------- |
| `400`    | Correct the request first.                           |
| `401`    | Correct or replace the credential first.             |
| `403`    | Resolve customer or Plan access first.               |
| `409`    | Inspect the idempotency error code before retrying.  |
| `422`    | Correct the operation input or remote image first.   |
| `429`    | Wait for quota availability or `resetAt`.            |
| `502`    | Retry cautiously when appropriate for your workflow. |
| `503`    | Retry cautiously with bounded exponential backoff.   |
| `500`    | Retry cautiously and preserve `requestId`.           |

## Idempotency retry contract

`POST /v1/evidence/reports` supports safe retries through the optional
`Idempotency-Key` header.

For the same customer, product, operation, idempotency key, and normalized
request, a retry returns the original successful Evidence Report result without
retrieving remote images again, creating another report, invoking the provider
again, or recording another usage unit.

Requests without `Idempotency-Key` remain supported but do not receive this
safe-retry guarantee.

See [Report contract](/operational-evidence/guides/report-contract) for the
complete idempotency behavior.
