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.


HTTP Status Codes

StatusTypical MeaningExample error_code
400 Bad RequestMalformed JSON or unsupported payloadjson_parse_error
401 UnauthorizedMissing / invalid auth, expired token, or invalid API keyauthentication_failed
402 Payment RequiredSpend cap reached (paid) or plan allowance exhausted (Free); billable writes are blockedspend_cap_reached, plan_exhausted
403 ForbiddenAuthenticated but lacks permissions (role mismatch), or a structural plan limit was exceededpermission_denied, plan_limit_exceeded
404 Not FoundEnvironment, collection, resource, etc. does not exist or you cannot access itenvironment_not_found, resource_not_found, locale_not_found
405 Method Not AllowedWrong HTTP verb for the endpointmethod_not_allowed
422 Unprocessable ContentRequest failed validation or endpoint-specific business rulesvalidation_error, trailing_slash_required, protected_environment_cannot_be_deleted
429 Too Many RequestsPer-plan requests-per-minute rate limit exceeded; carries a Retry-After header and is not billedrate_limited
500 Internal Server ErrorUnexpected backend errorinternal_server_error

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);
  }
}

  • Limits — structural plan quotas and the 403 plan_limit_exceeded contract.
  • Billing — metering, spend cap, and the 402 responses (spend_cap_reached, plan_exhausted).

Was this page helpful?