The go~mus API responds to errors with a matching HTTP status and a JSON body in one of two shapes. This page covers the codes you actually see in practice, what the body looks like, and how to react cleanly per error class.
Status codes at a glance
| Code | When | Retry useful? |
|---|---|---|
200 / 201 | Success | - |
202 Accepted | Async process running (e.g. document virus scan), result later | Yes, after a short backoff |
401 Unauthorized | Token missing, expired or invalid | No, swap the token |
403 Forbidden | Token valid, but the permission is missing | No, try with another user or permission |
404 Not Found | Resource does not exist, or invalid route | No, check the id |
410 Gone | Resource was there but is no longer available (e.g. expired document) | No |
422 Unprocessable Entity | Validation or business-logic error (the most common failure class) | Conditionally - only if the cause is transient (see below) |
5xx | Server-side error | Exponential backoff, 3-5 attempts |
We do not currently use 429 rate limiting actively. Even so, plan for moderate concurrency when batching.
Body format
Two variants, depending on the endpoint:
Simple error
{
"error": "reservation not valid anymore"
}
The most common form - especially for 422 and 401. A short, human-readable message, no machine code, no locale negotiation. Log it, but map your own end-user texts for display.
Error with field list
{
"error": "customer could not be saved",
"errors": [
"Email can't be blank",
"Phone is too short (minimum is 5 characters)"
]
}
For validation errors on nested objects (customers, orders, requests, contacts) you also get an errors array with field validations. The strings are not machine-readable - they come straight from ActiveRecord validations. Map your own end-user texts.
Error classes with examples
401 - token problems
{ "error": "authentication error; no customer or no user present" }
Common causes:
- Authorization header missing
- Bearer token typo or expired
- Token from a different instance (tokens are instance-scoped)
React: do not retry, fetch a fresh token. Useful for debugging: log the last 4 characters of the token used, so you can see which token was active.
422 - validation and business logic
{ "error": "reservation is invalid: ticket is required, quantity must be > 0" }
or
{ "error": "reservation not possible" }
Typical cases:
- Required fields missing in POST/PUT body
- Values out of range (date in the past, quantity > capacity)
- State conflicts: reservation expired, slot sold out in the meantime, order already finalized
- Configuration conflicts: ticket not bookable in current auth context, tour without matching language
Reaction depends on the sub-case:
- Required fields missing → fix frontend validation, no retry
- Slot sold out → re-fetch availability, ask user to pick again, no blind retry
- Reservation expired → create a new reservation
- Race condition (two parallel order creates on the same slot) → retry once with a small delay, then give up
404 - resource missing
{ "error": "invalid route" }
or
{ "error": "customer not found" }
Classics:
- Numeric id that does not exist (or got archived)
- Token in URL that never existed or is gone (
reservation_token) - Typo in the route
React: do not retry. Show "not found" appropriately in the UI.
410 - resource is gone
Rare, but it happens with documents/:id/download and similar: the document was on the server but is now out of reach (e.g. retention period elapsed). React: do not retry, tell the user "no longer available".
5xx - server-side error
When the go~mus instance briefly does not respond (deployment, database hiccup), you see 502/503/504. React: exponential backoff, 3-5 attempts, then surface an error in the UI. With non-idempotent calls (POST order, POST reservation) be careful with retries - the server may have received and processed the call already.
Idempotency and retry
Rule of thumb:
| HTTP method | Safe to retry? |
|---|---|
GET, HEAD, OPTIONS | Yes |
PUT, DELETE | Yes |
POST | No, without extra protection |
What that means for POST /orders, POST /reservations, POST /requests:
- Recognize success unambiguously on the client:
200/201with a valid response body, otherwise do not resend. - On timeout, check before retrying: orders and requests have list endpoints (
GET /orders,GET /requests) where the entry can be found if the server did create it. For reservations there is no lookup - in doubt let the previous token expire and create a new one. - Use foreign ids: when syncing customers or orders with your system, use
foreign_idas an external key - the server can detect duplicates on retry, or you can in your system.
More in Best Practices.
What to log
Per failed call, at minimum:
- HTTP status, full URL path with query params (token values masked)
- Body excerpt of the response (
erroranderrorsarray) - Timestamp (UTC) and correlation id if you set one
- Last 4 characters of the bearer token (for identification without exposing the secret)
That makes debugging in support cases dramatically easier for both sides.
Related pages
- Authentication - token types, context awareness
- Best Practices - caching, idempotency, logging
- Concepts - order lifecycle, reservation, permissions