Every refusal has the same shape: an HTTP status, and a JSON body with a fixed error code and a fixed sentence.
"error" : "role_required" ,
"message" : "this needs the client_admin role"
error is one of a fixed set, below. Branch on it, not on the message.
message is a fixed sentence for people. It never carries a key, a token, a body, or the value that was refused.
Some refusals add fields: parameter names the query parameter that was wrong, attribute the body field, limit and retry_after the limit that was reached.
A refusal is sent with Cache-Control: no-store, and with Retry-After where waiting will help.
Code
Means
Do
bad_query
A path or query parameter, or a body field, is missing or not valid
Fix the request. parameter says which, where it can
LightHouse answers a request that fails validation with 400 bad_query , never with 422.
Code
Status
Means
Do
unauthenticated
401
No session and no valid key: missing, revoked, expired, or from another tenant
Sign in, or issue a key in this tenant
mfa_required
403
The session does not prove a second factor
Sign in again with a second factor
not_authorised
403
This identity has no grant in this tenant
Ask a client_admin for access
role_required
403
The route needs a higher role
See Roles
person_required
403
The route acts only for a signed-in person; a machine key never can
Do it in LightHouse, as a person
commands_disabled
403
This command type is not switched on for your tenant
A client_admin switches it on under Setup and admin → Commands
managed_by_aal
403
This provider connection is managed by Atlantic
Ask Atlantic to change it
assistant_off, avatars_off
403
The feature is switched off for your tenant
A client_admin switches it on
Code
Means
not_found
No such resource in your tenant
unknown_client
No tenant at this address
flow_not_found
A connector approval has expired (after 15 minutes); start again
Code
Means
version_conflict
What you are changing changed since you read it. Read it again, then write
conflict
That already exists
connector_coming
The connector is not built yet. See Connect your estate
route_unavailable
That way of connecting is not available for this connector yet
adoption_closed
The marketplace listing is not open to new adoption
details_required, invoice_terms, invoice_collection, no_customer, no_platform_price, invoice_not_open, nothing_pending, mirror_not_needed
Billing: the step you asked for does not apply to your tenant’s terms or state; the message says why
Code
Means
invalid
The body is not a JSON object, is too large, or a field’s value is not allowed. attribute names the field
guard_rail
The change breaks a rule LightHouse fixes, such as a key’s expiry or the staff role. rule names it
provider_refused
The provider would not accept the credential or approval; the message names the provider
secret_refused
A field that should hold an identifier looks like a credential
Code
Means
Do
assistant_capped
The assistant has reached its limit
Wait, or raise its limit
See Rate limits .
Code
Status
Means
Do
internal
500
A fault on our side. Nothing was returned
Retry later; tell Atlantic if it persists
stripe_failed
502
The payment provider did not accept the request; nothing was charged
Retry
deployment_waking
503
Your ephemeral deployment is waking (“Waking your deployment…”)
Retry after Retry-After (5 seconds)
tier_b_unavailable
503
Your content cannot be read in place right now: your deployment’s content reader is not connected
Check the reader is running
provider_unavailable
503
The provider could not be reached
Retry shortly
secret_store_unavailable
503
Your tenant’s key is not available, so a credential cannot be sealed
Retry; tell Atlantic if it persists
commands_unavailable, assistant_unavailable, avatars_unavailable, billing_unavailable, exports_unavailable, identity_unavailable
503
That part of LightHouse is not set up where your tenant runs, or not reachable
Retry later; tell Atlantic if it persists
A handler command that LightHouse accepted can still be refused by your deployment. That refusal is not an HTTP error: the command’s status becomes refused, with a reason from a fixed set, such as type-disabled, role, not-an-approver, bounds, raise-not-allowed, not-waiting, evidence-changed or gate-not-passed. Read it with GET /api/v1/commands/{command_id}. See Handlers .
A tool refusal arrives as a tool error carrying the same sentence, for example for role_required. Any other failure arrives as a generic message that quotes nothing. See MCP .