Errors
Error envelope, status codes, and what each common code means.
Every error from the fastmon API uses the same envelope. The HTTP status code tells you the shape; the code inside tells you the cause.
Envelope
{
"error": {
"code": "validation_error",
"message": "domain is required",
"request_id": "01HZY…",
"doc_url": "https://docs.fastmon.eu/errors/validation_error",
"details": { "field": "domain" }
}
}| Field | Required | Notes |
|---|---|---|
code | yes | Stable machine-readable code. Safe to switch on. |
message | yes | Human-readable. May change for the same code as we improve clarity. |
request_id | usually | Same value as the X-Request-ID response header. |
doc_url | optional | Link to a docs page that explains the code in depth. |
details | optional | Code-specific. Validation errors put the offending field here. |
Switch on code in client code; render message to humans. The code is part of our public contract; message may be reworded for clarity at any time.
Status codes
| Status | What it means |
|---|---|
400 | The body or query was malformed. See code for specifics. |
401 | Authentication failed or wasn't provided. See Authentication. |
402 | Allowed, but the organization is not entitled to it. See Entitlement. |
403 | Authenticated, but not allowed. Missing permission, wrong organization, or paused resource. |
404 | Resource doesn't exist, or you can't see it. We don't leak existence. |
409 | Conflict: usually a uniqueness violation or a state-machine clash. |
422 | Validation failed. details.field carries the offending field. |
429 | Rate-limited. Wait and retry; Retry-After is set. |
code values
The full list, grouped by category.
Auth
| Code | Meaning |
|---|---|
unauthenticated | No credentials, or they didn't validate. |
invalid_credentials | Email/password combination didn't match. |
two_factor_required | Login passed step one; a TOTP code is needed to complete the session. |
invalid_two_factor_code | The TOTP code was wrong or expired. |
session_expired | The session cookie is no longer valid. Log in again. |
invalid_token | Short-lived opaque token (email verification, password reset, OAuth state, 2FA temp) was invalid or expired. Distinct from session_expired on purpose: don't bounce the user to /login on a stale reset link. |
credential_expired | The API key is genuine but past its expires_at. Create a new key. |
Authorization
| Code | Meaning |
|---|---|
forbidden | Authenticated, but not allowed for this action. |
organization_access_denied | The token/user can't access this organization. |
site_access_denied | The token/user can't access this site. |
admin_required | The action requires platform-admin privileges. |
permission_denied | In the organization, but without the permission the route needs. details.permission names it; for relationship rules (handing out the owner role) details.required_role does. Also answered when an API key was not issued for this organization. |
Not found
| Code | Meaning |
|---|---|
organization_not_found | Organization doesn't exist or isn't visible to you. |
site_not_found | Site doesn't exist or isn't visible to you. |
release_not_found | Release marker doesn't exist on this site. |
rule_not_found | Notification rule doesn't exist. |
member_not_found | Organization member doesn't exist. |
invite_not_found | Invite doesn't exist or has been consumed. |
partner_not_found | Partner relationship doesn't exist. |
user_not_found | User doesn't exist or isn't visible to you. |
resource_not_found | Catch-all for resources without a more specific code. |
Conflict / state
| Code | Meaning |
|---|---|
email_already_registered | Sign-up attempted with an email that already has an account. |
site_already_exists | Domain is already registered in this organization. |
release_already_exists | A release with this version already exists on the site. |
organization_inactive | The organization is suspended. |
user_inactive | The user account is disabled. |
already_member | Tried to invite or add someone who's already in the organization. |
billing_partner_exists | Billing-partner relationship is already set; remove the existing one first. |
circular_partner_forbidden | Partner relationship would create a cycle. |
self_transfer_forbidden | Can't transfer ownership to yourself. |
last_owner | Demoting or removing the organization's last owner would leave nobody who can administer it. |
conflict | Catch-all conflict without a more specific code. |
Entitlement
Answered with 402. The permission was there; what the organization is on does not cover the action. Creating a production application, or promoting a development one to production, is the route a script meets these on. A development application is free and takes no slot.
| Code | Meaning |
|---|---|
billing_required | No active subscription, valid trial, or free plan. Nothing to create against. |
trial_expired | The trial has lapsed and no payment has taken its place. |
app_limit_reached | The plan's production application slots are used up. Buy a slot, upgrade, or create the application as development. |
billing_disabled | Billing is switched off platform-wide. Nothing for the caller to fix. |
Input
| Code | Meaning |
|---|---|
validation_error | A field failed validation. details.field names it. |
unsupported_operation | The operation isn't supported in this context (e.g. wrong storage mode). |
invalid_cursor | The pagination cursor was malformed or has expired. |
Rate limit
| Code | Meaning |
|---|---|
rate_limited | Too many requests. Retry-After is set on the response. |
Plan / entitlement
| Code | Meaning |
|---|---|
plan_insufficient | The action requires a higher plan tier than the organization is on. |
ai_disabled | AI features are turned off for this organization or globally. |
synthetic_disabled | Synthetic Monitoring isn't enabled for this organization. An owner can turn it on under Organization Settings. |
Resource limits / upstream
| Code | Meaning |
|---|---|
beacon_buffer_full | The beacon ingestion buffer is saturated. Retry shortly. |
clickhouse_unavailable | The analytics store is temporarily unavailable. |
upstream_unavailable | A required upstream service didn't respond. Retry with backoff. |
oauth_error | An OAuth provider returned an error or the state didn't validate. |
Feature lifecycle
| Code | Meaning |
|---|---|
feature_removed | The endpoint or feature has been removed and no longer accepts requests. |
Generic
| Code | Meaning |
|---|---|
internal_error | Server bug. Include request_id when reporting. |
not_ready | Service is starting up or briefly draining. Retry shortly. |
Retrying
| Status | Retry? |
|---|---|
429 | Yes: wait Retry-After seconds. |
502 / 503 / 504 | Yes: exponential backoff. Usually deploy-window blips. |
Other 5xx | Once. If it persists, report with request_id. |
4xx | No. The request is wrong; fix and resend. |
Reporting an error
Always include the request_id when you contact support. It lets us pull the matching server log in seconds. Without it, debugging starts from "describe what time it happened roughly," which is much slower.
The request ID is on:
- the
X-Request-IDresponse header, error.request_idin the JSON body,- the response of every successful request too (for chain correlation).
Common surprises
404is intentionally ambiguous. "I can't see this resource" and "this resource doesn't exist" both return404. We don't want to leak existence to unauthorized clients.- Validation errors stop at the first failure. A payload with two bad fields reports one. Fix and resend to see the next.
Related
- API overview
- Authentication:
401/403origins