Errors
Every error produced by the API itself uses the RFC 9457 problem body
below. In addition to the standard type · title · status · detail
fields, we provide a machine-readable stable identifier code and a
tracing identifier requestId.
Branch client logic on the code value, not on the HTTP status code or
the detail string. detail is a human-readable description whose wording
may change, while code is preserved under the additive-only policy.
Requests that never reach the API are answered by the API gateway in front
of it. The common edge errors use the same problem body — with a code and
a requestId (also sent as the X-Request-Id header) — but they are not
recorded in API request logs, because there is no
organization to file them under:
- Authentication runs before routing. A missing or malformed key returns
401 unauthorizedwhatever the path — the edge does not reveal whether a path exists before the key has been checked. - A path or method that does not exist returns
404 route_not_foundonce the key is valid (check the URL first — including the/v1prefix — and the HTTP method). Earlier builds answered these with the gateway's own403 {"message":"Missing Authentication Token"}, or with a signature error that echoed a hash of theAuthorizationheader; both bodies are gone. - A key that is not enabled for the API stage returns
403 unauthorized; rps throttling and the edge quota net return429 rate_limited/429 quota_exceeded. 403 forbiddenis the edge's safety net for the rare 4xx conditions that match none of the above (an authorizer deny policy, or a gateway-level rejection of the request itself). You should not see it in normal use.- The
Content-Typeheader on these edge responses isapplication/jsoneven though the body is a problem object (the API's own errors useapplication/problem+json) — do not key off the media type.
Rare edge conditions (for example a request body over the gateway's size
limit) still use the gateway's plain {"message": "…"} body with no code.
So parse the body defensively: treat a response with no code field as an
unknown error and fall back to the HTTP status.
Response format
{
"type": "https://developers.fleeta.io/errors/insufficient_scope",
"title": "Forbidden",
"status": 403,
"code": "insufficient_scope",
"detail": "This operation requires the 'gps:export' scope.",
"requestId": "d1af1825-1117-461e-9a04-a0c756019e75",
"requiredScope": "gps:export"
}
| Field | Description |
|---|---|
type | Error-kind URI (…/errors/{code}) — opening it takes you to that code's reference section in this guide. For client branching, still use code |
title | Human-readable summary of the HTTP status (Forbidden, Bad Gateway, Service Unavailable, Gateway Timeout, etc.) — never branch on title |
status | HTTP status code |
code | Machine-readable stable code — the basis for client branching |
detail | Human-readable description (wording may change — do not hardcode) |
requestId | Request tracing ID — quote it when you contact Fleeta support (also sent as the X-Request-Id header, on both success and error responses) |
Depending on the error kind, extension fields are added: requiredScope
(insufficient_scope), bucket/limit/used (quota_exceeded),
allowed (list of allowed enum values), maxRangeDays/rangeDays
(invalid_field — GPS export range over the cap), min/max (invalid_field — a
numeric field outside its bounds), psns (unknown_psn), subscriptionStatus
(webhook_not_suspended), and so on.
Extension fields are declared per code in the OpenAPI spec as Problem<Code>
schemas (ProblemCloudOnly, ProblemQuotaExceeded, ProblemInvalidField, … —
see Models). Every field listed under a code below is in
that code's schema, and nothing else is ever sent; codes without a Problem<Code>
schema carry no extension fields. The JavaScript SDK narrows them
with error.is(code) (0.6.0+).
All error codes
Every code the API can return. Each code links to its
reference section below — the same section the
type URI (https://developers.fleeta.io/errors/{code}) redirects to.
Four responses are shared by every operation, because authentication,
scope, subscription and call-cap checks run before any handler: 401 unauthorized, 403 (insufficient_scope · subscription_inactive ·
quota_exceeded), 429 (monthly_limit_exceeded · rate_limited) and
500 internal_error. The API reference lists them on each operation
alongside that operation's own errors.
| code | HTTP | Meaning |
|---|---|---|
invalid_parameter | 400 | Query parameter format/range/enum error |
invalid_body | 400 | Request body is not valid JSON |
invalid_cursor | 400 | The after cursor is corrupted or was issued by a different endpoint — discard it and re-iterate from the first page |
unauthorized | 401 · 403 | API key missing/invalid (401), or not enabled for this API stage (403, edge) — Authentication |
forbidden | 403 | Edge safety net (rare) — the request was rejected at the API edge for a reason other than a missing route, a bad or disabled key, or throttling |
insufficient_scope | 403 | Key lacks a required scope (requiredScope extension field) |
group_not_allowed | 403 | The requested groupId is outside the key's pinned group set — Tenant Isolation |
tier_invalid | 403 | The key was issued with a retired tier — re-issue the key |
subscription_inactive | 403 | The org's subscription lapsed — the key is paused until the subscription is renewed (same key resumes automatically) |
camera_limit_exceeded | 403 | The dashcam is registered beyond the subscription's camera count (entitlement: over_limit in GET /v1/devices), or registering more dashcams would exceed it — video, SD-card access, recall and remote commands are locked; everything stored stays readable. Raise the camera count in the Fleeta web viewer; the same key picks it up within 5 minutes (cameraPosition · cameraLimit · registeredCameras · requiredCameraLimit extension fields) |
quota_exceeded | 403 · 429 | Monthly volume quota (e.g. transfer_bytes, export_job) exceeded or not offered on the plan (bucket/limit) — distinct from the monthly API call cap below. Enforcement is fail-closed: if the quota store cannot determine remaining allowance, the request is rejected. The API edge's usage-plan safety net emits the same code with status 429 — retrying either variant is pointless |
recall_limit_exceeded | 403 | Declared for the upstream VOD-download allowance, which is not applied to API-key calls today — SD Recordings |
route_not_found | 404 | Method/path does not exist — answered at the API edge after the key is validated (a missing or invalid key returns 401 first), so it never appears in API request logs |
group_not_found | 404 | The requested groupId does not exist in this organization (a group that exists but has no dashcams returns 200 with an empty list) — Tenant Isolation |
device_not_found | 404 | Device not found (including outside company scope — Tenant Isolation) |
event_not_found | 404 | Event not found |
geofence_not_found | 404 | Geofence not found |
webhook_not_found | 404 | Webhook subscription not found |
trip_not_found | 404 | Trip not found |
job_not_found | 404 | GPS Export job or media recall job not found |
audit_log_not_found | 404 | Audit log not found |
video_not_available | 404 | No event video for the requested channel |
settings_not_found | 404 | No settings snapshot is stored yet for the device |
file_not_found | 404 | The requested recording does not exist on the device SD card |
not_found | 404 | A referenced resource (e.g. the vehicle behind a psn filter) was not found |
job_already_completed | 409 | Attempted to cancel an already-completed Export job |
device_busy | 409 | The dashcam is busy with another transfer — it handles one transfer at a time, so this is a normal, temporary condition. A Retry-After header carries the seconds to wait before retrying the same request — SD Recordings |
device_offline | 409 | The device is offline — operations that talk to the device (SD file list/read/delete, commands, recall start) cannot run |
export_in_progress | 409 | An export job is already in progress for this organization |
sd_card_absent | 409 | No SD card is present in the device |
concurrency_limit_exceeded | 409 | Concurrent export job limit reached (limit/active) — wait for the active job to finish or cancel it |
webhook_not_suspended | 409 | POST /v1/webhooks/{webhookId}/reactivate on a subscription that is not suspended — nothing to reactivate (subscriptionStatus) |
geofence_unsupported_shape | 409 | PUT /v1/geofences/{geofenceId} without a shape on a geofence whose stored shape type this API cannot carry over — include a shape in the request or edit the geofence in the web viewer |
invalid_field | 422 | Request body field validation failed (allowed · maxRangeDays/rangeDays · min/max extension fields, depending on the field) |
unknown_psn | 422 | Contains a PSN outside the company/group scope (psns extension field) |
cloud_only | 422 | The PSN is a Wi-Fi-only dashcam (category: wifi in GET /v1/devices) — this feature is available only for cloud-connected dashcams (psn or psns, plus category extension fields). Not retryable |
unsupported_device | 422 | The dashcam model has no cloud SD-card command channel, so SD-card and recall features cannot run on it (psn · model extension fields). Not retryable |
no_scoped_devices | 422 | No devices within scope |
monthly_limit_exceeded | 429 | Monthly API call cap exceeded — further calls are blocked (no overage, no rollover) until the next billing month; Retry-After carries the seconds until the counter resets (limit/used/resetsAt extension fields) — Rate Limits |
rate_limited | 429 | Requests-per-second (rps) limit exceeded — retry after Retry-After |
internal_error | 500 | Internal server error — contact Fleeta support with the requestId |
configuration_error | 500 | A server-side backend/store is not configured — not a client fault |
not_implemented | 501 | Roadmap feature — not yet available |
write_delegation_not_configured | 501 | Device write backend not configured (environment not ready) |
export_not_configured | 501 | GPS Export backend not configured (environment not ready) |
device_settings_not_configured | 501 | Device settings write backend not configured (environment not ready) |
device_settings_read_not_configured | 501 | Device settings read backend not configured (environment not ready) |
device_command_not_configured | 501 | Device command backend not configured (environment not ready) |
media_recall_not_configured | 501 | Media recall backend not configured (environment not ready) |
delegate_failed | 502 | Internal delegation call failed — retryable (the documented request constraints are rejected as 4xx before delegation) |
upstream_unavailable | 503 | An internal upstream (e.g. the group roster service) could not be reached — retryable; scoped queries fail closed rather than returning silently empty results |
command_timeout | 504 | The device did not respond in time (it may have gone offline) |
query_timeout | 504 | GET /v1/safety-events/stats could not aggregate the requested period within the time budget — narrow from/to (or scope with groupId). Not retryable as is |
One code you may see that is intentionally not in this table:
invalid_psn in POST /v1/devices bulk-registration responses is a
per-item failure code inside the failed[] array of a success
response — it is never a top-level problem+json code.
Code reference
Each section below is the target of the corresponding
https://developers.fleeta.io/errors/{code} URI. Example detail strings
are real values from the API reference — remember that wording may change,
so branch on code only.
400 Bad Request
invalid_parameter
A query parameter has a bad format, range, or enum value.
- Status:
400 - Example
detail:'from' must be earlier than 'to'. - Extension fields:
allowed— the accepted values, present when an enum value (e.g. an eventtype) is rejected.
invalid_body
The request body could not be parsed as JSON.
- Status:
400 - Example
detail:Request body is not valid JSON.
invalid_cursor
The after pagination cursor is corrupted, or it was issued by a different
endpoint (every endpoint tags its own cursors, so a cursor from
/v1/events is rejected by /v1/audit-logs). Discard the stored cursor
and re-iterate from the first page. A rejected cursor never silently
restarts the listing — the request fails so that a paging loop cannot run
forever.
- Status:
400 - Example
detail:The 'after' cursor is invalid or was issued by a different endpoint.
401 Unauthorized
unauthorized
The API key is missing or invalid — see
Authentication. Returned both at the API edge
(missing/malformed Authorization header) and by the API itself. Because
the edge authenticates before it routes, an unknown path called with a
missing or invalid key also gets this 401 — not 404.
The API edge also uses this code with status 403 when a syntactically
valid key is not enabled for the API stage — typically a key issued
moments ago (propagation) or one that predates its usage plan. Retry
shortly; if it persists, re-issue the key from Management > Open API.
- Status:
401(missing/invalid) ·403(not enabled for this stage — edge only) - Example
detail:A valid API key is required (Authorization: Bearer flt_...).
403 Forbidden
forbidden
The edge's safety net: the request was rejected at the API edge for a reason
that is neither a missing route (404 route_not_found), a missing, bad or
disabled key (401 / 403 unauthorized), nor throttling. In practice that
means an authorizer deny policy, or a gateway-level rejection of the request
itself. It is rare — you should not see it in normal use. Not raised by the
API itself, which uses the more specific codes below. If you do see it, check
the HTTP method against the API reference; this API never uses AWS request
signing, so an Authorization header is only ever a Bearer API key.
- Status:
403 - Example
detail:Access to this API is denied for the presented credentials.·The request was rejected at the API edge (DEFAULT_4XX). If the path exists, check the HTTP method against the API reference; this API never uses AWS signatures.
insufficient_scope
The API key does not carry a scope this operation requires. Scopes follow your subscription tier at request time — after a plan upgrade the new scopes apply on the next authorization (the gateway caches authorizations for up to five minutes), with no reissue. The only exception is a key issued with an explicit scope list: that list never grows on its own, so issue a new key that includes the scope. See Rate limits › plan changes.
- Status:
403 - Example
detail:This operation requires the 'gps:export' scope. - Extension fields:
requiredScope— the missing scope.
group_not_allowed
The requested groupId is outside the key's pinned group set — see
Tenant Isolation.
- Status:
403 - Example
detail:This API key is not allowed to access group 'grp_…'.
tier_invalid
The key was issued with a tier that has since been retired. Re-issue the key.
- Status:
403 - Example
detail:This API key was issued with a retired tier. Please re-issue the key.
subscription_inactive
The organization's Fleeta subscription is no longer active, so the API key is paused. This is checked at call time (re-evaluated roughly every 5 minutes) — you do not need to re-issue the key. Renew the subscription in the Fleeta webviewer and the same key starts working again within minutes. Sandbox test keys and free-tier keys are never affected.
- Status:
403 - Example
detail:Your subscription is no longer active, so this API key is paused. Renew the subscription in the Fleeta webviewer to resume — the same key starts working again within minutes. - Extension fields:
reason—no_active_subscription|plan_not_eligible
camera_limit_exceeded
The dashcam is registered beyond the subscription's camera count, or the
request would register more dashcams than the subscription covers. Seats go
to the most recently registered cloud dashcams; the rest are over_limit —
still listed in GET /v1/devices (with entitlement and cameraPosition)
and still readable everywhere data is already stored (location, trips,
events, thumbnails, statistics, geofence alerts, GPS export), but video
URLs, SD-card access, recall and remote commands answer this code. This
mirrors the Fleeta web viewer, where such dashcams are shown greyed out.
Nothing needs to be re-configured: raise the camera count in the Fleeta web
viewer (Account › Subscription) and the same key picks it up on the next
authorization — within 5 minutes, no reissue. requiredCameraLimit is the
count that would cover the dashcam (or the registration). For
POST /v1/devices the check is all-or-nothing and remainingSeats says how
many dashcams can still be registered. Sandbox test keys are never locked.
Every list that carries dashcams also returns a cameras block
(limit · registered · overLimit) so you can see the organization's seat
position without a failing call; limit is null when the subscription could
not be resolved for that request — nothing is locked in that case. The full
rules — seat order, what stays readable, the entitlement filter and the
reports' covered default — are in
Rate Limits › Camera seats.
- Status:
403 - Example
detail:Device '7XBPK0BE00000012' is dashcam 12 of 13 registered, but the subscription covers 10. Raise the camera count to 12 or more in the Fleeta web viewer (Account › Subscription) — this key picks it up within 5 minutes, no reissue. Its location, trips, events, thumbnails, geofences and GPS export stay available; video, SD-card access, recall and remote commands need a covered camera. - Extension fields:
cameraLimit,registeredCameras,requiredCameraLimit(always);psn,cameraPosition(single-device routes);psns,remainingSeats(POST /v1/devices).
quota_exceeded
A monthly volume quota (e.g. transfer_bytes, export_job) is exceeded, or
the bucket is not offered on the current plan (limit: 0) — distinct from
the monthly API call cap. Enforcement is
fail-closed: if the quota store cannot determine remaining allowance, the
request is rejected. Retrying is pointless — check bucket and consider a
plan upgrade. See Rate Limits.
- Status:
403(the API-edge usage-plan safety net returns the same code with status429) - Example
detail:Monthly quota for 'transfer_bytes' exceeded (limit=56.0 GB). Please upgrade your plan.— a byte bucket is stated in GB here;limit/usedstay raw bytes - Extension fields:
bucket,limit,used— in the bucket's unit, bytes fortransfer_bytes(usedis omitted when the quota store rejected the increment before the counter could be read). The API-edge usage-plan variant (status429) carries none of these fields — it is generated at the gateway, before the quota store is consulted.
recall_limit_exceeded
The subscription's VOD-download allowance is used up for the current period — see SD Recordings.
This code is declared because the upstream token service can return it, but the
VOD-download allowance is enforced only for the Fleeta web viewer. Calls made
with an API key are not counted against it, so you are unlikely to see this
code. What does bound recall volume: the media:recall scope (Pro and above),
the camera-seat check (403 camera_limit_exceeded), one transfer per dashcam at
a time, and the vehicle's own data plan.
- Status:
403 - Example
detail:The recall (VOD download) allowance for this plan has been used up for the current period.
404 Not Found
404 does not distinguish "does not exist" from "outside my company's
scope" — see Tenant Isolation.
route_not_found
The method/path combination does not exist. Answered at the API edge before
the request reaches the API — but only after the key has been validated
(a missing or invalid key returns 401 unauthorized first, so the edge never
reveals whether a path exists to an unauthenticated caller). It carries a
requestId (and the X-Request-Id header) but is not recorded in
API request logs. Check the URL — including the /v1
prefix — and the HTTP method against the API reference.
- Status:
404 - Example
detail:No route matches this method and path. Check the URL (including the /v1 prefix) against the API reference.
group_not_found
The groupId you passed does not exist in your organization — usually a typo
or a group that has since been deleted. It is returned by every operation that
accepts a groupId filter.
Three outcomes are possible for groupId, and they are checked in this order:
| Situation | Response |
|---|---|
Not in grp_<id> form | 400 invalid_parameter |
| Well-formed, but outside the key's pinned group set | 403 group_not_allowed |
| Well-formed and allowed, but no such group in the organization | 404 group_not_found |
A group that does exist but has no dashcams assigned is not an error — it
returns 200 with an empty list. Before this code existed, a missing group
returned that same empty list, so a typo looked like "no vehicles" and a
/v1/fleet/locations/feed poller kept receiving zero events forever. See
Tenant Isolation.
Group membership is cached for up to 10 minutes, so a group created seconds
ago can still answer 404 for a short while — retry, or list the groups with
GET /v1/groups to confirm the ID.
- Status:
404 - Example
detail:Group 'grp_zzz999' was not found in this organization.
device_not_found
The device (PSN) was not found, including PSNs outside the company scope.
- Status:
404 - Example
detail:Device with PSN '7XBPK0BE00000003' was not found.
event_not_found
The event was not found.
-
Status:
404 -
Example
detail:Event 'evt_0032' was not found.Event IDs stored before the events endpoints started returning one row per event still resolve — they are mapped to the event that file belongs to. Read the ID back from
GET /v1/eventswhen you can, rather than relying on that fallback.
geofence_not_found
The geofence was not found.
- Status:
404 - Example
detail:Geofence 'gf_0002' not found.
webhook_not_found
The webhook subscription was not found.
- Status:
404 - Example
detail:Subscription 'wh_demo0001' not found.
trip_not_found
The trip was not found.
- Status:
404 - Example
detail:Trip 'trip_7XBPK0BE00000001_01' not found.
job_not_found
The GPS Export job or media recall job was not found.
- Status:
404 - Example
detail:Export job 'gpsexp_a1b2c3d4e5f6' not found.·Recall job 'mrcl_a1b2c3d4e5f6' was not found.
audit_log_not_found
The audit log entry was not found.
- Status:
404 - Example
detail:Audit log 'al_0003' not found.
video_not_available
The requested channel is not one of the event's channels. Read channels
from the event and ask for a channel that is listed there — do not assume
front. The same code answers GET /v1/events/{eventId}/thumbnail when the
requested channel has no thumbnail.
- Status:
404 - Example
detail:No 'front' channel video is available for event 'evt_0032'.
settings_not_found
No settings snapshot has been stored yet for the device.
- Status:
404 - Example
detail:No settings snapshot is stored yet for device '7XBPK0BE00000003'.
file_not_found
The requested recording does not exist on the device SD card.
- Status:
404 - Example
detail:The requested recording does not exist on the device SD card.
not_found
A resource referenced by the request (e.g. the vehicle behind a psn
filter on event listing, or the device of a geofence alert) was not found.
- Status:
404 - Example
detail:Vehicle '…' was not found.
409 Conflict
job_already_completed
The Export job is already completed and can no longer be canceled.
- Status:
409 - Example
detail:A completed job cannot be canceled. - Extension fields:
jobId.
device_busy
The dashcam is busy with another transfer — it handles one transfer at a
time, so this is a normal, temporary condition. A Retry-After header
carries the seconds to wait before retrying the same request — see
SD Recordings.
- Status:
409(+Retry-Afterheader) - Example
detail:The dashcam handles one transfer at a time and is currently busy with another operation. Retry in a few seconds. - Extension fields:
psn.
device_offline
The device is offline, so an operation that talks to the device (SD file list/read/delete, command delivery, recall start) cannot run.
- Status:
409 - Example
detail:Device '7XBPK0BE00000003' is offline; cannot read its SD card.
export_in_progress
An export job is already in progress for this organization.
- Status:
409 - Example
detail:An export job is already in progress for this organization. - Extension fields:
activeJobId,activeStatus— when the active job is known.
sd_card_absent
No SD card is present in the device.
- Status:
409 - Example
detail:No SD card is present in the device.
concurrency_limit_exceeded
The concurrent export job limit is reached. Wait for the active job to finish or cancel it.
- Status:
409 - Example
detail:Concurrent export job limit reached (limit=1). Wait for the active job to finish or cancel it. - Extension fields:
limit,active.
webhook_not_suspended
POST /v1/webhooks/{webhookId}/reactivate was called on a subscription that is
not suspended. Only subscriptions the dispatcher suspended after repeated
delivery failures (the exact rule is in Webhooks) can be
reactivated — an active one needs nothing. Not retryable.
- Status:
409 - Example
detail:Subscription 'wh_demo0001' is not suspended (status 'active'); nothing to reactivate. - Extension fields:
subscriptionStatus— the subscription's currentstatus(active|suspended).
geofence_unsupported_shape
PUT /v1/geofences/{geofenceId} was called without a shape on a geofence
whose stored shape type is not one the API can carry over unchanged (the
supported types are circle, rectangle, polygon and polyline). Because
an update replaces the whole shape, the request is rejected rather than
silently rewriting it. Include a shape in the request, or edit the geofence
in the web viewer. Not retryable as is.
- Status:
409 - Example
detail:This geofence has an unknown shape type; edit it in the web viewer.
422 Unprocessable Entity
invalid_field
A request body field failed validation.
- Status:
422 - Example
detail:url must be a URL starting with https://. - Example
detail(GPS export):'to' may be at most 90 days after 'from' (UTC calendar days; got 91). - Example
detail(webhook rotate-secret):graceSeconds must be an integer between 0 and 86400. - Example
detail(SD-card recording —GET /v1/devices/{psn}/sd-files/{filename}/videoandPOST /v1/media/recall-jobs):filename must be the main recording name 'YYYYMMDD_HHMMSS_XY.mp4' as returned by the SD file list (select the sub-stream with quality=sub, not by appending 'S' to the filename).— a…S.mp4sub-stream name, a malformed name or malformed percent-encoding in the path. - Extension fields:
allowed— accepted values, when an enum-like field (e.g. webhookevents, orqualityofPOST /v1/media/recall-jobs) is rejected;maxRangeDays(always90) andrangeDays— when the GPS exportfrom–torange exceeds the cap;minandmax— the accepted bounds when a numeric field (e.g.graceSecondsofrotate-secret) is outside its range. Nothing else is sent (device-side result codes are never included).
unknown_psn
The request contains PSNs outside the company/group scope.
- Status:
422 - Example
detail:Request contains PSNs that are not in scope. - Extension fields:
psns— the offending PSNs.
cloud_only
The PSN belongs to a Wi-Fi-only dashcam. Wi-Fi-only dashcams are listed by
GET /v1/devices with category: wifi so your inventory is complete, but they
never connect to the cloud — so anything that needs cloud connectivity
(location, trips, events, video, SD-card access, recall, settings, reboot,
GPS export) answers this code instead of 404. Filter the device list with
category=cloud to get the dashcams those features apply to. Retrying does not
help; the device would need to be a cloud-connected model.
- Status:
422 - Example
detail:Device '7XBPK0BE00000003' is a Wi-Fi-only dashcam (category 'wifi'). This feature is available only for cloud-connected dashcams (category 'cloud'). - Extension fields:
psn(single-device routes) orpsns(multi-device requests — GPS export or geofencedevices; the offending PSNs), andcategory(alwayswifi).
unsupported_device
The dashcam is cloud-connected, but its model does not implement the cloud
SD-card command channel, so the SD-card and recall features cannot reach it:
GET /v1/devices/{psn}/sd-files, the per-recording metadata, video and delete
routes, and POST /v1/media/recall-jobs. Older models such as DR750S-2CH,
DR900S-2CH and DR590X-2CH Plus are in this group. Everything that does not
talk to the SD card — the device list and detail, events, trips, GPS export —
works normally on these dashcams. Retrying does not help; the model would need
to be a newer one.
- Status:
422 - Example
detail:Device '7XBPK0BE00000069' (model DR750S-2CH) does not support cloud SD-card commands. This feature needs a CloudNative3-capable dashcam. - Extension fields:
psnandmodel(the model as registered,nullwhen the record carries none).
no_scoped_devices
There are no devices within the key's scope to operate on.
- Status:
422 - Example
detail:No devices in scope.
429 Too Many Requests
Handle 429 by status code first, then branch on code — see
Rate Limits.
monthly_limit_exceeded
The monthly API call cap is exceeded — further calls are blocked (no overage, no rollover) until the next billing month.
- Status:
429(+Retry-Afterheader — seconds until the counter resets) - Example
detail:Monthly API call limit reached (limit=3500). Upgrade your plan or wait until the next billing month. - Extension fields:
limit,used,resetsAt.
rate_limited
The requests-per-second (rps) limit is exceeded. Returned by the API edge
(the request does not reach the API), so these responses do not appear in
API request logs. Retry after Retry-After.
- Status:
429(+Retry-Afterheader) - Example
detail:Request rate limit exceeded. Retry after the Retry-After interval.
500 Internal Server Error
internal_error
An unexpected server error. Retry with backoff; if it persists, contact
Fleeta support with the requestId.
- Status:
500 - Example
detail:An internal error occurred.
configuration_error
A server-side backend/store (e.g. quota storage, group scoping) is not
configured. This is not a client fault — contact Fleeta support with the
requestId.
- Status:
500 - Example
detail:Quota storage is not configured (QUOTA_TABLE missing).
501 Not Implemented
Roadmap features and environments whose backing service is not wired up yet. These are server-side conditions — the request itself is well-formed.
not_implemented
The feature is on the roadmap and not yet available.
- Status:
501 - Example
detail:'…' is on the roadmap (Phase 3) and not yet available. - Extension fields:
op— the attempted write operation on geofence paths:update(create/update) ordelete.
write_delegation_not_configured
The device write backend is not configured (environment not ready).
- Status:
501 - Example
detail:Device write delegation is not configured (env DEVICE_REGISTER_FN not set). - Extension fields:
op.
export_not_configured
The GPS Export backend is not configured (environment not ready).
- Status:
501 - Example
detail:Export delegation is not configured — env GPS_EXPORT_FN (gpsExportApi Lambda) must be set.
device_settings_not_configured
The device settings write backend is not configured (environment not ready).
- Status:
501 - Example
detail:Device settings delegation is not configured (env DEVICE_SETTINGS_FN not set).
device_settings_read_not_configured
The device settings read backend is not configured (environment not ready).
- Status:
501 - Example
detail:Device settings read delegation is not configured (env DEVICE_SETTINGS_READ_FN not set).
device_command_not_configured
The device command backend is not configured (environment not ready).
- Status:
501 - Example
detail:Device command delegation is not configured (env DEVICE_COMMAND_FN not set).
media_recall_not_configured
The media recall backend is not configured (environment not ready).
- Status:
501 - Example
detail:Media recall is not configured (env VOD_TOKEN_FN not set).
502 Bad Gateway
delegate_failed
An internal delegation call failed. Transient — retry with backoff; if it
persists, contact Fleeta support with the requestId.
A GPS export whose range exceeds the documented 90-day cap does not produce this code — it is 422 invalid_field.
- Status:
502 - Example
detail:Device settings read returned an unexpected payload. - Extension fields:
op— the attempted write operation, when applicable (register·update·vehiclefor devices,deletefor geofences). Upstream status codes and messages are never included — quoterequestIdto support.
503 Service Unavailable
upstream_unavailable
An internal upstream (e.g. the group roster service) could not be reached. Retryable — scoped queries fail closed rather than returning silently empty results.
- Status:
503 - Example
detail:Group information is temporarily unavailable. Please retry.
504 Gateway Timeout
command_timeout
The device did not respond to a command in time (it may have gone offline).
- Status:
504 - Example
detail:The device did not respond in time (it may have gone offline). - Extension fields:
psn.
query_timeout
GET /v1/safety-events/stats aggregates the whole period in one pass, so a
wide period over a large fleet can run past the request's time budget. Rather
than return a partial — and therefore wrong — set of totals, the API answers
504 query_timeout.
Not retryable as is: the same request costs the same again. Narrow from/to
(a week at a time is comfortable even for fleets of several hundred dashcams),
or scope the query with groupId, and add the pieces up on your side.
GET /v1/events and GET /v1/events/feed never answer with this code. They
page instead: when a rare type makes the scan run long, they return the rows
found so far with pagination.hasMore: true, and you follow nextCursor —
see Safety Events.
- Status:
504 - Example
detail:The statistics query did not finish within the time budget. Narrow the period with 'from'/'to' (or scope it with 'groupId') and try again.
Code branching example
const res = await fetch(url, { headers: { Authorization: `Bearer ${API_KEY}` } });
if (!res.ok) {
// Rare edge errors (e.g. an oversized body) still use the gateway's plain
// {"message":"..."} body with no `code`, so read the code first and only then
// decide how to handle it.
const problem = await res.json().catch(() => ({}));
// 429 is retryable ONLY for rate_limited / monthly_limit_exceeded. The edge
// usage-plan net emits quota_exceeded as 429 too, and that one never clears
// by retrying — branching on status alone would loop until the month rolls over.
if (res.status === 429 && problem.code !== 'quota_exceeded') {
return retryWithBackoff(res); // see the rate-limits guide
}
switch (problem.code) {
case 'insufficient_scope':
// prompt for key reissuance — problem.requiredScope carries the needed scope
break;
case 'quota_exceeded':
// retrying is pointless (403 or 429) — check problem.bucket and suggest a plan upgrade
break;
case 'invalid_cursor':
cursor = undefined; // discard the stored cursor and restart from the first page
break;
case 'device_not_found':
// PSN not in company scope — existence is not distinguished
break;
case 'device_busy':
// the dashcam is mid-transfer — wait Retry-After seconds, then retry the same request
break;
case 'delegate_failed':
case 'internal_error':
// transient error — retry with backoff; if it persists, contact developer support (sales@fleeta.io) with requestId
break;
case 'route_not_found':
// wrong path or HTTP method — fix the URL; this never appears in API request logs
break;
default:
// no `code` at all → a raw gateway error (e.g. an oversized body); log the status
console.error(res.status, problem.code, problem.detail, problem.requestId);
}
}
With the official SDK, every error is wrapped as an OpenApiError
(status · code · detail · requestId) — see SDKs.
e.is('cloud_only') narrows e.body to that code's fields (psns, category),
and e.isKnown() narrows e.code to the documented set for an exhaustive switch (SDK 0.6.0+).
Caveats / best practices
- Do not match on the
detailstring. Wording may be improved over time. Branch oncode; displaydetail. - The
typeURI is a stable identifier and a working link — opening it lands on the code's reference section in this guide. For branching, still comparecode, not thetypestring. - When reporting an issue, include the
requestIdso it can be matched directly against server logs. 404does not distinguish "does not exist" from "outside my company's scope" — see Tenant Isolation.4xxmeans the request must be fixed — do not retry it as is. Retry targets are429(with backoff),409 device_busy(afterRetry-After), and5xx(transient errors).
Related links
- Rate Limits — handling 429 and
quota_exceeded - Authentication — 401/403
- Tenant Isolation — what 404 means
- SDKs —
OpenApiError