Error Messages
Common error responses, what they mean, and how to fix them.
When a request fails, Surfact returns a standard HTTP status code and a JSON body describing the problem. Four body shapes are in production, so branch on the HTTP status code, not on the shape of the body:
| Shape | Example | Where it shows up |
|---|---|---|
{code, message} | {"code": 404001, "message": "The request was not found."} | Authentication failures (401), missing objects (404), retention violations (403003) |
{detail} | {"detail": "You do not have permission to perform this action."} | Malformed JSON bodies, permission-denied 403s, and out-of-range pagination 404s |
{code, message, detail} | {"code": 400001, "message": "Validation Error.", "detail": {"aggregation": ["\"stddev\" is not a valid choice."]}} | Field validation (400001) and method-not-allowed (405001). detail is sometimes a string, sometimes an array, sometimes an object keyed by field name |
{description, status, message} | {"description": "Bad Request", "status": 400, "message": "Invalid request body: VariablesAggregationRequestSchema. Error: …"} | Query- and body-schema validation on the Data API, including start/end range errors. message embeds the internal schema name and a pydantic URL — log it, but don't parse it |
Authentication errors
Surfact returns a single authentication-failure code. A missing X-Auth-Token header, an empty header, an empty ?token= and a wrong or expired token all produce the same 401:
{
"code": 401002,
"message": "Incorrect authentication credentials."
}There is no separate code for "credentials not provided", so the response won't tell you whether the token was absent or simply wrong — check that the header is being sent on your side. See Authentication.
Error codes
| Code | HTTP | Meaning | Fix |
|---|---|---|---|
400001 | 400 | Validation Error | Read detail — it names the offending fields or values |
401002 | 401 | Incorrect authentication credentials | The token is missing, wrong, or expired; generate a fresh one (tokens are valid 6 hours) |
403003 | 403 | The requested time range is older than the account's retention limit (24 months by default) | Clamp start to the retention window — see Data retention below |
404001 | 404 | The request was not found | Check the label/id and the ~ prefix — see Identifiers — Key vs. ID |
405001 | 405 | HTTP method not allowed | Check the verb; the Data API aggregation and raw-series endpoints are POST-only |
A 403 without a code — {"detail": "You do not have permission to perform this action."} — means the token is valid but the account lacks permission for that resource. A wrong or missing token returns 401, never 403.
Data retention
Requests whose time range reaches further back than the account's retention window (24 months by default) are rejected outright, not clipped — the in-window portion of the range is not returned either:
curl -X GET 'https://api.surfact.com/api/v1.6/variables/<variable_id>/values/?start=1634792400000&end=1634794200000' \
-H 'X-Auth-Token: <your_token>'{
"code": 403003,
"message": "The time range you're trying to retrieve is older than the retention limit in your account (24 months). Please contact your administrator to increase your data retention period."
}This applies to start alone, end alone, and both together, on /values/, on the aggregated-data endpoints, and on both Data API POST endpoints. It is easy to trip by accident: start and end are milliseconds, and passing POSIX seconds by mistake lands you decades in the past. Clamp start yourself before querying a long reefer history.
Validation & request errors
| Situation | Response | Fix |
|---|---|---|
| Malformed JSON body | {"detail": "JSON parse error - …"} (HTTP 400) | Validate your JSON; check commas, quotes, and types |
| Failed field validation | {"code": 400001, "message": "Validation Error.", "detail": { … }} (HTTP 400) | detail is keyed by field name; fix the fields it names |
| Invalid enum on a Data API POST | {"description": "Bad Request", "status": 400, "message": "Invalid request body: …"} (HTTP 400) | The message lists the accepted values; pick one of those |
| Page number past the last page | {"detail": "Invalid page."} (HTTP 404) | Stop paging when next is null rather than incrementing blindly |
| Unknown device/variable label | {"code": 404001, "message": "The request was not found."} (HTTP 404) | Check the label/id and the ~ prefix — see Identifiers — Key vs. ID |
A filter "isn't working"
If a filter has invalid syntax, Surfact ignores it silently and returns all objects — you won't get an error. If a query returns more than you expect, re-check the __ separator and attribute name. See the FAQ.
Cold-chain example
A request to read a tracker's temperature with an expired token:
curl -X GET 'https://api.surfact.com/api/v1.6/variables/<variable_id>/values/?page_size=1' \
-H 'X-Auth-Token: expired_token'{
"code": 401002,
"message": "Incorrect authentication credentials."
}Generate a new token and retry. For the full status-code reference, see Response Codes.
Updated 7 days ago