Error Codes
All error codes returned by the endpoint and storage API. Error codes are immutable -- they will never change once published.
# Error Codes
All error codes returned by the endpoint and storage API. Error codes are immutable -- they will never change once published.
## Endpoint Errors
| Code | HTTP | Description |
|------|------|-------------|
| `ENDPOINT_NOT_FOUND` | 404 | No endpoint matches the URL slug and HTTP method |
| `ENDPOINT_DISABLED` | 403 | Endpoint exists but is currently disabled |
| `ENDPOINT_CONFIG_ERROR` | 500 | Endpoint has a misconfiguration (bad collection reference, missing field) |
| `ENDPOINT_VARIABLE_ERROR` | 400 | A `{{...}}` template variable could not be resolved |
| `ENDPOINT_LOOKUP_EMPTY` | 404 | A lookup step found no matching record |
| `ENDPOINT_PARTIAL_FAILURE` | 400 | Some write steps succeeded but others failed |
| `ENDPOINT_RETRYABLE_WRITE_FAILURE` | 429/503 | No write steps committed because storage was temporarily busy or unavailable; retry after the response `retryAfterSeconds` |
## Collection Errors
| Code | HTTP | Description |
|------|------|-------------|
| `ENDPOINT_ONLY` | 403 | Direct collection access requires a secret key; route client actions through an endpoint |
| `NOT_FOUND` | 404 | Collection or data key not found |
| `SCHEMA_VALIDATION_FAILED` | 400 | Data does not match the collection schema |
| `PAYLOAD_TOO_LARGE` | 413 | Request body exceeds 1MB |
| `INVALID_KEY` | 400 | Key format invalid (must be alphanumeric, hyphens, underscores) |
| `INVALID_JSON` | 400 | Request body is not valid JSON |
| `INVALID_BODY` | 400 | Body must be a JSON object |
## Authentication Errors
| Code | HTTP | Description |
|------|------|-------------|
| `UNAUTHORIZED` | 401 | Invalid or missing API key |
| `SBOX_AUTH_FAILED` | 401 | s&box auth token verification failed |
| `SECRET_KEY_REQUIRED` | 401 | Endpoint, query, or server-only route requires a verified Network Storage secret key |
| `SECRET_KEY_INVALID` | 401 | Supplied secret key is invalid, disabled, or belongs to another project |
| `FORBIDDEN` | 403 | Secret key lacks the required read/write/execute permission for this route |
| `PROJECT_DISABLED` | 403 | Project is disabled in settings |
### docsUrl Field
Every error response includes a `docsUrl` field pointing to the relevant documentation page for that error:
```yml
ok: false
error:
code: SBOX_AUTH_FAILED
message: s&box auth token verification failed.
docsUrl: https://sboxcool.com/wiki/network-storage-v3/sbox-auth
```
Use the `docsUrl` to find troubleshooting steps and examples for the specific error your integration is hitting.
## Management API Errors
These errors are returned by the Management API (`/v3/manage/...`), used by the Sync Tool and CI/CD integrations.
Authentication requires two headers: `x-api-key` (secret key, `sbox_sk_*`) and `x-public-key` (public key, `sbox_ns_*`).
The Project ID is part of the URL, so management requests always identify a project with all three pieces: `projectId` + public key + secret key.
| Code | HTTP | Description |
|------|------|-------------|
| `MISSING_SECRET_KEY` | 401 | `x-api-key` header missing or does not start with `sbox_sk_` |
| `MISSING_PUBLIC_KEY` | 401 | `x-public-key` header missing or does not start with `sbox_ns_` |
| `INVALID_SECRET_KEY` | 401 | Secret key not recognized or does not belong to this project |
| `INVALID_PUBLIC_KEY` | 401 | Public key not recognized or does not belong to this project |
| `PROJECT_MISMATCH` | 401 | Secret key belongs to a different project |
| `KEY_DISABLED` | 401 | API key is disabled |
| `KEY_UPGRADE_REQUIRED` | 401 | Secret key uses old format -- regenerate from dashboard |
| `FORBIDDEN` | 403 | Key lacks the required permission scope |
| `PROJECT_NOT_FOUND` | 401 | No project with the given ID |
All 401 responses include an `expected` object with the required headers:
```yml
ok: false
error: MISSING_SECRET_KEY
message: Missing or invalid x-api-key header. Must be a secret key (sbox_sk_*).
expected:
headers:
x-api-key: sbox_sk_... (128-character secret key)
x-public-key: sbox_ns_... (public API key)
```
## Rate Limit & Quota Errors
| Code | HTTP | Description |
|------|------|-------------|
| `RATE_LIMIT_DAILY` | 429 | Daily save limit exceeded (collection-level, deprecated) |
| `RATE_EXCEEDED` | 429 | Field rate limit exceeded (v2 rule or schema _maxPerHour) |
| `QUOTA_EXCEEDED` | 429 | Monthly quota exceeded (requests, bandwidth, or storage). See [Quotas & Usage](/wiki/network-storage-v3/quotas-and-usage) |
## Data Integrity Errors
| Code | HTTP | Description |
|------|------|-------------|
| `STALE_SAVE` | 409 | Save sequence is behind the server's (another session saved first) |
| `UNIQUE_CONFLICT` | 409 | A `_unique` field value is already claimed by another player |
| `RECORD_LIMIT_REACHED` | 409 | Maximum save slots reached for this player |
| `RECORD_NOT_FOUND` | 404 | Save slot record not found |
| `RECORD_DELETE_DISABLED` | 403 | Record deletion is not enabled for this collection |
| `COLLECTION_TYPE_MISMATCH` | 400 | Wrong endpoint for collection type (e.g. append on per-player) |
| `COLLECTION_DELETE_WEBSITE_ONLY` | 400 | Collection definition deletion must be performed from the website dashboard with two-step verification |
| `GLOBAL_WRITE_DENIED` | 403 | Global collections are append-only |
## Workflow Errors
Workflow conditions return custom error codes defined in the workflow's `onFail` config:
```yml
error:
code: INSUFFICIENT_CURRENCY
message: Not enough currency
flagged: false
severity: null
```
The `flagged` field indicates if the player was flagged for review. The `severity` field is `"warning"` or `"critical"` when flagged.
## Handling Errors in s&box
```csharp
var response = await NetworkStorage.CallEndpoint( "purchase-item", new
{
item_id = "sword_gold"
} );
if ( !response.HasValue )
{
// The library logs endpoint errors. Show a failure state in your UI.
Log.Warning( "Purchase failed." );
return;
}
// Success -- parse the response body returned by your endpoint
Log.Info( "Purchase successful!" );
```
## HTTP Status Code Reference
| Status | Meaning | Should Retry? |
|--------|---------|--------------|
| 200 | Success | N/A |
| 400 | Bad request (validation, schema, condition) | No -- fix the request |
| 401 | Authentication failed | No -- check API key/token |
| 403 | Forbidden (disabled, endpoint-controlled) | No |
| 404 | Not found | No |
| 409 | Conflict (stale save, unique violation) | Yes -- reload data first |
| 413 | Payload too large | No -- reduce body size |
| 429 | Rate limited | Yes -- wait and retry |
| 500 | Server error | Yes -- retry with backoff |
| 502 | Upstream storage error | Yes -- retry |
| 503 | Service restarting (deploy in progress) | Yes -- retry after `Retry-After` seconds |
### 503 Service Restarting
During deployments, the API returns `503 Service restarting` with a `Retry-After: 3` header for new requests. In-flight requests are allowed to complete (up to 30 seconds). The service uses zero-downtime blue/green deployments via nginx, so 503s are rare and brief.