DOCS / API / ERRORS
API errors
Every response under /api/ with a status of 400 or above returns the same JSON
object. Branch on error. Never parse message.
#The envelope
{
"error": "method_not_allowed",
"message": "GET is not supported on this endpoint. Allowed: POST.",
"request_id": "d22e3678-8a18-4aa3-ab86-bfd374080269",
"documentation_url": "https://glassmkr.com/docs/api/errors#method_not_allowed",
"retryable": false,
"retry_after_seconds": null,
"details": [{ "allowed_methods": ["POST"] }]
} - error is a stable machine code. It is the only field you should make decisions from. Codes are added over time; an unrecognised one should be treated by its HTTP status.
- message is for humans and logs. The wording can change in any release.
- request_id matches the
X-Request-Idresponse header. Quote it when reporting a problem. - retryable says whether an identical retry could plausibly succeed. A client mistake is never retryable, because retrying it unchanged only burns quota.
- retry_after_seconds is set when the server can say how long to wait. The
Retry-Afterheader carries the same value. - details is a list of structured extras, empty when there are none.
This holds for the whole namespace, including a 404 for an unknown path and a 405 for the wrong verb. Those two used to return the HTML app shell and untyped plain text respectively, which is exactly the shape an autonomous client cannot recover from.
#Deciding what to do
The envelope is designed so a client can decide without reading prose:
retryable: trueand aretry_after_seconds: wait that long, then retry the same request.retryable: truewithout a delay: retry with your own backoff.retryable: falseon a 401: get a different credential. The same one will fail identically.retryable: falseon a 403: stop. This credential is not permitted the operation, and repeating it will not change that.retryable: falseon a 400 or 422: fix the request fromdetailsbefore sending anything again.retryable: falseon a 409: re-read the current state before deciding, because yours is stale.
#Code reference
19 codes, 5 of them retryable. Each anchor here is what documentation_url points at.
| Code | Status | Retryable | Meaning |
|---|---|---|---|
invalid_request | 400 | no | The request was malformed or a field failed validation. Read details, correct the request, do not retry unchanged. |
unauthenticated | 401 | no | No credential was presented, or the one presented was not accepted. Obtain or refresh a credential; retrying the same one will fail identically. |
payment_required | 402 | no | The account's billing state blocks this operation. |
forbidden | 403 | no | The credential is valid but not permitted this operation, usually a scope or ownership boundary. Do not retry; a different credential is needed. |
not_found | 404 | no | The addressed resource does not exist, or is not visible to this credential. |
unknown_endpoint | 404 | no | No API route matches this path at all. Check /api/openapi.json for the routes this deployment serves. |
method_not_allowed | 405 | no | The route exists but not for this HTTP verb. details carries allowed_methods, and the Allow header repeats it. |
conflict | 409 | no | The operation conflicts with current state, for example a stale version or an operation already in progress. Re-read state before deciding. |
gone | 410 | no | The resource existed and has been removed permanently. |
payload_too_large | 413 | no | The request body exceeded the accepted size. |
unsupported_media_type | 415 | no | The Content-Type is not accepted by this route. |
unprocessable | 422 | no | The request parsed but could not be acted on as given. |
rate_limited | 429 | yes | Too many requests. Wait retry_after_seconds, or the Retry-After header, before retrying. |
pro_required | 403 | no | Legacy code from the retired Free/Pro split, kept because old clients may still branch on it. Nothing in the API is tier-gated as of August 2026. |
internal_error | 500 | yes | An unexpected server fault. Safe to retry with backoff; report request_id if it persists. |
upstream_error | 502 | yes | A dependency failed. Retry with backoff. |
unavailable | 503 | yes | The service is temporarily unable to handle the request. Retry with backoff. |
upstream_timeout | 504 | yes | A dependency did not answer in time. Retry with backoff. |
request_failed | 4xx | no | Fallback for a 4xx status with no more specific code. Treat as non-retryable and read message. |
#OAuth is deliberately different
The MCP authorization endpoints under /oauth/ return the shape RFC 6749
mandates, { error, error_description }, not the envelope above. An OAuth
client is entitled to expect the standard shape, so those routes are excluded on purpose
rather than by oversight.