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

# sovseal API Error Codes and HTTP Status Reference

> Complete reference for sovseal API error response envelope format and all error codes, grouped by HTTP status from 400 through 503.

<style>
  {`
      main, article, .prose {
        margin-left: 2.5cm !important;
        margin-right: 2.5cm !important;
      }
      `}
</style>

When any sovseal API request fails, the edge function returns a flat JSON error envelope with a machine-readable error code and the appropriate HTTP status. Your client should branch on the `error` field — not the HTTP status alone — since multiple distinct conditions can share a status code. The error envelope format is consistent across all endpoints.

## Error Envelope Format

Every error response has the following structure:

```json theme={null}
{
  "error": "sequence_gap"
}
```

Some errors include additional fields. For example, split-brain conflicts attach a `conflicts` array:

```json theme={null}
{
  "error": "split_brain_detected",
  "conflicts": [
    {
      "sequence_number": 10,
      "existing_block_hash": "f5a2b3c4...",
      "attempted_block_hash": "e3b0c442..."
    }
  ]
}
```

***

## Error Code Reference

### 400 Bad Request

The request body or query parameters were structurally invalid or logically inconsistent.

| Error Code                | Description                                                                                                                             |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_json`            | The request body could not be parsed as valid JSON.                                                                                     |
| `missing_field:<field>`   | A required field is absent. The field name is appended after the colon (e.g. `missing_field:agent_id`, `missing_field:ciphertext_b64`). |
| `bad_policy_hash`         | The `policy_hash` field is not a valid 64-character hex string.                                                                         |
| `bad_payload_hash`        | The `client_payload_hash` field is not a valid 64-character hex string.                                                                 |
| `bad_sequence`            | The `sequence_number` value is invalid (e.g. negative or non-integer).                                                                  |
| `genesis_violation`       | `sequence_number` is `0` but `parent_snapshot` is not `null`.                                                                           |
| `missing_parent_snapshot` | `sequence_number` is greater than `0` but no `parent_snapshot` was provided.                                                            |
| `byte_size_mismatch`      | The decoded length of `ciphertext_b64` does not match the submitted `byte_size`.                                                        |
| `missing_since_param`     | The `since` query parameter is required but was not included on a `/replay` request.                                                    |

### 401 Unauthorized

The request could not be authenticated.

| Error Code                        | Description                                                                                               |
| --------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `missing_or_invalid_bearer_token` | The `Authorization` header is absent or does not follow the `Bearer <TOKEN>` format.                      |
| `session_jwt_required`            | The endpoint requires a dashboard session JWT but received an agent API key (`sov_live_` or `sov_proj_`). |
| `invalid_session`                 | The session JWT (issued by WorkOS AuthKit) is expired or failed verification against the remote JWKS.     |
| `invalid_api_key`                 | The SHA-256 hash of the submitted `sov_live_` key was not found in the database.                          |
| `api_key_revoked`                 | The API key was previously revoked and is no longer valid for any operation.                              |
| `owner_not_found`                 | The user account linked to this API key does not exist on the platform.                                   |

### 402 Payment Required

The authenticated account does not have sufficient billing credits to complete the operation.

| Error Code             | Description                                                                                                                                                        |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `insufficient_credits` | The key owner's per-write credit balance (`credits_milli`) is insufficient. **This gate applies to `sov_live_` keys only** — `sov_proj_` tokens are never debited. |

<Warning>
  As of this writing, `credits_milli` defaults to `0` on every account and is not funded by subscribing to a paid plan through Polar — the current subscription webhook updates plan/quota fields but does not grant credits. If you are hitting this error on a paid key, that is very likely a billing-configuration issue on our end rather than something you can resolve by upgrading. Contact `enterprise@sovseal.com` if you see this.
</Warning>

### 403 Forbidden

The request was authenticated but the caller lacks permission for the requested action.

| Error Code           | Description                                                                                                  |
| -------------------- | ------------------------------------------------------------------------------------------------------------ |
| `not_a_team_member`  | The authenticated user is not a member of the requested ZK team.                                             |
| `forbidden_role`     | The member's assigned role (e.g. `read`) does not permit this operation.                                     |
| `telemetry_disabled` | The user's dashboard settings have telemetry opted out — the telemetry endpoint will not accept submissions. |
| `no_grant`           | The member has not accepted their team invite or does not hold a wrapped DEK grant for the requested team.   |

### 404 Not Found

The requested resource does not exist.

| Error Code               | Description                                                                                  |
| ------------------------ | -------------------------------------------------------------------------------------------- |
| `no_snapshots_for_agent` | The agent has no confirmed snapshots to restore (genesis snapshot has not been written yet). |
| `snapshot_not_found`     | No confirmed snapshot exists for the requested sequence number.                              |
| `route_not_found`        | The request path does not map to any active handler in the edge function.                    |
| `no_replication_entries` | Returned by `GET /head` when the agent has not pushed any replication chunks yet.            |

### 409 Conflict

The request conflicts with existing persisted state.

| Error Code             | Description                                                                                                                                                                                           |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sequence_conflict`    | A snapshot already exists at this sequence number with a different payload hash.                                                                                                                      |
| `sequence_gap`         | The submitted `sequence_number` for a snapshot is not exactly `latest + 1` — gaps in the sequence chain are not permitted.                                                                            |
| `split_brain_detected` | A duplicate sequence number insertion was attempted with a different `block_hash`, indicating two offline clients diverged on the same sequence slot. The response body includes a `conflicts` array. |

### 413 Payload Too Large

The submitted ciphertext exceeds the allowed size limit.

| Error Code             | Description                                                                                                   |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `ciphertext_too_large` | The decoded `ciphertext_b64` for a snapshot upload exceeds **256 KB** (`262,144` bytes, `MAX_PAYLOAD_BYTES`). |
| `chunk_too_large`      | The decoded `ciphertext_b64` for a replication chunk exceeds 256 KB (`MAX_CHUNK_BYTES`).                      |

### 503 Service Unavailable

The storage backend is temporarily unreachable.

| Error Code            | Description                                                                                                                                                                               |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `storage_unavailable` | The object storage backend (Supabase Storage) was unreachable during the upload. Any credits debited for the failed operation are automatically refunded. Retry with exponential backoff. |
