Errors
All Management API errors follow one consistent JSON envelope: message, error_code, and detail. Knowing how to interpret each field helps you surface actionable feedback to your users and react appropriately in automation.
Response Envelope
Error object
{
"message": "Permission denied",
"error_code": "permission_denied",
"detail": null
}
- Name
message- Type
- string
- Description
Human-readable description. Use for logs or user-facing copy.
- Name
error_code- Type
- string
- Description
Stable machine-readable code. Rely on this field for branching.
- Name
detail- Type
- object | array | null
- Description
Optional extra context. Validation errors populate this field with field-level issues; most other errors return
null.
The machine-readable key is always error_code (never error). Branch your automation on error_code, not on message.
HTTP Status Codes
| Status | Typical Meaning | Example error_code |
|---|---|---|
| 400 Bad Request | Malformed JSON or unsupported payload | json_parse_error |
| 401 Unauthorized | Missing / invalid auth, expired token, or invalid API key | authentication_failed |
| 402 Payment Required | Spend cap reached (paid) or plan allowance exhausted (Free); billable writes are blocked | spend_cap_reached, plan_exhausted |
| 403 Forbidden | Authenticated but lacks permissions (role mismatch), or a structural plan limit was exceeded | permission_denied, plan_limit_exceeded |
| 404 Not Found | Environment, collection, resource, etc. does not exist or you cannot access it | environment_not_found, resource_not_found, locale_not_found |
| 405 Method Not Allowed | Wrong HTTP verb for the endpoint | method_not_allowed |
| 422 Unprocessable Content | Request failed validation or endpoint-specific business rules | validation_error, trailing_slash_required, protected_environment_cannot_be_deleted |
| 429 Too Many Requests | Per-plan requests-per-minute rate limit exceeded; carries a Retry-After header and is not billed | rate_limited |
| 500 Internal Server Error | Unexpected backend error | internal_server_error |
Endpoints often expose specific codes such as protected_environment_cannot_be_deleted, api_prefix_exists, or collection_already_connected_to_api. The HTTP status always matches the error type even when the code changes.
Validation Errors (422)
Field-level validation issues set error_code to validation_error and populate detail with an array of issues:
Validation error
{
"message": "Invalid data was provided",
"error_code": "validation_error",
"detail": [
{
"type": "string_too_short",
"loc": ["name"],
"msg": "String should have at least 1 character",
"input": ""
}
]
}
- Name
type- Type
- string
- Description
Error classifier (e.g.,
string_too_short,value_error).
- Name
loc- Type
- array
- Description
Location of the invalid field. Nested fields appear as multiple entries, such as
["fields", 0, "id"].
- Name
msg- Type
- string
- Description
Readable description that can be shown to end users.
- Name
input- Type
- any
- Description
Value that failed validation.
- Name
ctx- Type
- object | null
- Description
Optional metadata (for example,
{"min_length": 1}).
Content validation errors
Validating or publishing a revision checks the content against the collection's schema. These errors use a different item shape and are returned under detail.errors:
Content validation error
{
"message": "Content validation failed",
"error_code": "validation_failed",
"detail": {
"errors": [
{
"json_path": "$.title",
"message": "'title' is a required property",
"validator": "required",
"validator_value": ["title"]
}
]
}
}
The errors list is capped at 100 items. When more issues exist, the response also carries errors_truncated: true and errors_total with the full count. The response never includes the collection schema itself — fetch it via schema introspection or the versions API when you need it.
Endpoint-Specific Errors
Business rule violations use dedicated error codes so you can react explicitly:
Endpoint-specific error
{
"message": "Protected environment cannot be deleted",
"error_code": "protected_environment_cannot_be_deleted",
"detail": null
}
Examples include environment_toggle_error or too_many_versions. These codes indicate business constraints specific to the endpoint and are returned with detail: null. Structural plan limits are not endpoint-specific: they always use plan_limit_exceeded (see below).
Structural Limits (403)
Exceeding a structural plan quota (projects, environments, Flux APIs, locales, API keys, custom roles) returns 403 with plan_limit_exceeded. The detail object identifies the entity and its limit so you can prompt an upgrade:
Plan Limit Exceeded
{
"message": "Plan limit exceeded",
"error_code": "plan_limit_exceeded",
"detail": {
"entity": "flux_apis",
"limit": 5,
"current": 5,
"upgrade_url": "https://foxnose.net/billing"
}
}
See Limits for per-plan quotas.
Billing Gate (402)
The Management API authors content, so it consumes the Writes metering axis. When billing is gated, write requests return 402:
plan_exhausted— a Free plan has used up its allowance for a metering axis (here,writes). Only the exhausted axis is blocked.spend_cap_reached— a paid plan has hit its spend cap. Billable writes are blocked until the cap is raised or the cycle resets.
Plan Exhausted (Free)
{
"error_code": "plan_exhausted",
"axis": "writes",
"window_resets_at": "2026-08-01T00:00:00Z",
"upgrade_url": "https://foxnose.net/billing"
}
Spend Cap Reached (paid)
{
"error_code": "spend_cap_reached",
"cap_usd": 38.0,
"cycle_resets_at": "2026-08-01T00:00:00Z",
"raise_cap_url": "https://foxnose.net/billing"
}
Failed requests are never billed: 4xx, 5xx, 429, and OPTIONS responses do not consume allowance.
Trailing Slash Requirement
All Management API URLs must end with a trailing slash (/). For GET requests, a missing slash results in an automatic 301 redirect. For all other HTTP methods (POST, PUT, PATCH, DELETE), the API returns a 422 error:
Trailing slash error
{
"message": "URL must end with a trailing slash.",
"error_code": "trailing_slash_required",
"detail": null
}
Always ensure your request URLs include a trailing slash to avoid this error.
Handling Patterns
import axios from 'axios';
async function createFolder(payload) {
try {
const { data } = await axios.post('https://api.foxnose.net/v1/7c9h4pwu/collections/', payload);
return data;
} catch (error) {
if (!error.response) throw error;
const { status, data } = error.response;
if (status === 422 && data.error_code === 'validation_error') {
return data.detail.map((item) => ({
field: item.loc.join('.'),
message: item.msg,
}));
}
if (data.error_code === 'permission_denied') {
throw new Error('You lack access to this environment.');
}
throw new Error(data.message);
}
}