Skip to main content

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.

Errors produced at the API edge

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 unauthorized whatever 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_found once the key is valid (check the URL first — including the /v1 prefix — and the HTTP method). Earlier builds answered these with the gateway's own 403 {"message":"Missing Authentication Token"}, or with a signature error that echoed a hash of the Authorization header; both bodies are gone.
  • A key that is not enabled for the API stage returns 403 unauthorized; rps throttling and the edge quota net return 429 rate_limited / 429 quota_exceeded.
  • 403 forbidden is 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-Type header on these edge responses is application/json even though the body is a problem object (the API's own errors use application/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"
}
FieldDescription
typeError-kind URI (…/errors/{code}) — opening it takes you to that code's reference section in this guide. For client branching, still use code
titleHuman-readable summary of the HTTP status (Forbidden, Bad Gateway, Service Unavailable, Gateway Timeout, etc.) — never branch on title
statusHTTP status code
codeMachine-readable stable code — the basis for client branching
detailHuman-readable description (wording may change — do not hardcode)
requestIdRequest 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.

codeHTTPMeaning
invalid_parameter400Query parameter format/range/enum error
invalid_body400Request body is not valid JSON
invalid_cursor400The after cursor is corrupted or was issued by a different endpoint — discard it and re-iterate from the first page
unauthorized401 · 403API key missing/invalid (401), or not enabled for this API stage (403, edge) — Authentication
forbidden403Edge 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_scope403Key lacks a required scope (requiredScope extension field)
group_not_allowed403The requested groupId is outside the key's pinned group set — Tenant Isolation
tier_invalid403The key was issued with a retired tier — re-issue the key
subscription_inactive403The org's subscription lapsed — the key is paused until the subscription is renewed (same key resumes automatically)
camera_limit_exceeded403The 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_exceeded403 · 429Monthly 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_exceeded403Declared for the upstream VOD-download allowance, which is not applied to API-key calls todaySD Recordings
route_not_found404Method/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_found404The 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_found404Device not found (including outside company scope — Tenant Isolation)
event_not_found404Event not found
geofence_not_found404Geofence not found
webhook_not_found404Webhook subscription not found
trip_not_found404Trip not found
job_not_found404GPS Export job or media recall job not found
audit_log_not_found404Audit log not found
video_not_available404No event video for the requested channel
settings_not_found404No settings snapshot is stored yet for the device
file_not_found404The requested recording does not exist on the device SD card
not_found404A referenced resource (e.g. the vehicle behind a psn filter) was not found
job_already_completed409Attempted to cancel an already-completed Export job
device_busy409The 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_offline409The device is offline — operations that talk to the device (SD file list/read/delete, commands, recall start) cannot run
export_in_progress409An export job is already in progress for this organization
sd_card_absent409No SD card is present in the device
concurrency_limit_exceeded409Concurrent export job limit reached (limit/active) — wait for the active job to finish or cancel it
webhook_not_suspended409POST /v1/webhooks/{webhookId}/reactivate on a subscription that is not suspended — nothing to reactivate (subscriptionStatus)
geofence_unsupported_shape409PUT /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_field422Request body field validation failed (allowed · maxRangeDays/rangeDays · min/max extension fields, depending on the field)
unknown_psn422Contains a PSN outside the company/group scope (psns extension field)
cloud_only422The 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_device422The 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_devices422No devices within scope
monthly_limit_exceeded429Monthly 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_limited429Requests-per-second (rps) limit exceeded — retry after Retry-After
internal_error500Internal server error — contact Fleeta support with the requestId
configuration_error500A server-side backend/store is not configured — not a client fault
not_implemented501Roadmap feature — not yet available
write_delegation_not_configured501Device write backend not configured (environment not ready)
export_not_configured501GPS Export backend not configured (environment not ready)
device_settings_not_configured501Device settings write backend not configured (environment not ready)
device_settings_read_not_configured501Device settings read backend not configured (environment not ready)
device_command_not_configured501Device command backend not configured (environment not ready)
media_recall_not_configured501Media recall backend not configured (environment not ready)
delegate_failed502Internal delegation call failed — retryable (the documented request constraints are rejected as 4xx before delegation)
upstream_unavailable503An internal upstream (e.g. the group roster service) could not be reached — retryable; scoped queries fail closed rather than returning silently empty results
command_timeout504The device did not respond in time (it may have gone offline)
query_timeout504GET /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 event type) 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: reasonno_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 status 429)
  • 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 / used stay raw bytes
  • Extension fields: bucket, limit, used — in the bucket's unit, bytes for transfer_bytes (used is omitted when the quota store rejected the increment before the counter could be read). The API-edge usage-plan variant (status 429) 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.

Not applied to API keys today

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:

SituationResponse
Not in grp_<id> form400 invalid_parameter
Well-formed, but outside the key's pinned group set403 group_not_allowed
Well-formed and allowed, but no such group in the organization404 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/events when 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-After header)
  • 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 current status (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}/video and POST /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.mp4 sub-stream name, a malformed name or malformed percent-encoding in the path.
  • Extension fields: allowed — accepted values, when an enum-like field (e.g. webhook events, or quality of POST /v1/media/recall-jobs) is rejected; maxRangeDays (always 90) and rangeDays — when the GPS export fromto range exceeds the cap; min and max — the accepted bounds when a numeric field (e.g. graceSeconds of rotate-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) or psns (multi-device requests — GPS export or geofence devices; the offending PSNs), and category (always wifi).

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: psn and model (the model as registered, null when 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-After header — 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-After header)
  • 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) or delete.

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 · vehicle for devices, delete for geofences). Upstream status codes and messages are never included — quote requestId to 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 detail string. Wording may be improved over time. Branch on code; display detail.
  • The type URI is a stable identifier and a working link — opening it lands on the code's reference section in this guide. For branching, still compare code, not the type string.
  • When reporting an issue, include the requestId so it can be matched directly against server logs.
  • 404 does not distinguish "does not exist" from "outside my company's scope" — see Tenant Isolation.
  • 4xx means the request must be fixed — do not retry it as is. Retry targets are 429 (with backoff), 409 device_busy (after Retry-After), and 5xx (transient errors).