{
  "openapi": "3.1.1",
  "info": {
    "title": "Spillard Connect API",
    "description": "The Spillard Connect API gives your systems the data of your Spillard Live organizations: organizations, fleets, vehicles, drivers, devices, positions, events, journeys and video. Webhooks push new events, alarms, journeys, positions and finished video requests to your HTTPS endpoints as they happen.\n\n## Overview\n\n- Base URL: `https://connect.spillard.live`. Every route is under `/v1`.\n- JSON over HTTPS, field names in camelCase. Ids of organizations, fleets, vehicles, drivers, devices and API clients are UUIDs. Ids of events, device commands, media requests and webhook messages are opaque: text of up to 255 characters that you store and send back unchanged, never parse. Times are UTC, ISO 8601 (for example `2026-09-29T07:40:00Z`); a date without a time is `YYYY-MM-DD`. A field name ends with its unit where it has one (`durationSeconds`, `distanceMeters`, `speedKph` for kilometers per hour, `headingDegrees`). Positions are WGS84 decimal degrees.\n- Your client sees only the organizations, and the fleets, it was granted. `GET /v1/context` shows them.\n\n## Common tasks\n\n- Check what your client can read: `GET /v1/context` (needs no scope).\n- To request video from a device, send `POST /v1/media-requests` with `type: \"video\"`.\n- To request video of an event, send `POST /v1/events/{eventId}/media-requests`.\n- Follow a request with `GET /v1/media-requests/{mediaRequestId}`, then download its files from `GET /v1/media-requests/{mediaRequestId}/media`.\n- Get new events as they happen: the `spillard.event.raised.v1` webhook.\n- Read the latest vehicle positions: `GET /v1/vehicle-positions`, or the `spillard.telemetry.live.v1` webhook.\n\n## Authentication\n\n1. Get the client id and secret of an API client: an administrator creates one in the Spillard web app, or an existing client creates one with `POST /v1/api-clients`. Keep the secret on your servers.\n2. Exchange them for an access token at Spillard Identity:\n\n   ```\n   curl -sS https://identity.spillard.live/connect/token \\\n     -d grant_type=client_credentials \\\n     --data-urlencode \"client_id=$SPILLARD_CLIENT_ID\" \\\n     --data-urlencode \"client_secret=$SPILLARD_CLIENT_SECRET\"\n   ```\n\n   Without `scope` the token carries every scope of the client; add `scope` (space-separated) to ask for fewer.\n3. Send the token in `Authorization: Bearer <token>`. It is valid for `expires_in` seconds; get a new one when it runs out.\n\nEach operation names the scope it requires: `<resource>.read` to read, `<resource>.write` to change. The token's audience is `https://connect.spillard.live`. A missing, malformed or expired token is 401 `auth.token_invalid`; a missing scope is 403 `auth.scope_missing`.\n\n## Pagination\n\nLists return `items`, `hasMore` and `nextCursor`. To read the next page, send `nextCursor` as `cursor` with the same filters; the last page has `hasMore: false` and `nextCursor: null`. The cursor is opaque: an invalid one is 400 `validation.cursor_invalid`. `limit` sets the page size (each list states its range). `count=true` adds `totalCount`, the number of matching items, on the lists that take it. It costs an extra query and 5 extra rate-limit units, so send it on the first page only. `GET /v1/events` and `GET /v1/vehicle-positions` take no `count`; `GET /v1/devices` and `GET /v1/vehicles` answer 400 `validation.failed` when `count=true` comes with `connectivityStatus` or `lastReportedFrom`.\n\n## Limits\n\n- An array query parameter (`fleetIds`, `types` and the others) takes at most 100 values, and a media request at most 32 `channels`: more is 400 `validation.failed`.\n- A request body is at most 1 MB: a larger one is 400 `validation.failed`.\n- A time range spans at most 92 days for events, 90 days for journeys and commands, and 31 days for metrics: a longer one is 400 `validation.failed`.\n- The event lists (events, device events, journey events) return at most 500 items per page; every other list states its own maximum.\n\n## Errors\n\nEvery error is an RFC 9457 `application/problem+json` body with `status`, `title`, a stable `errorCode` and `retryable`. Branch on `errorCode`, never on `title` or `detail`; the ErrorCode schema lists every code with its meaning, and a code you do not know is handled by its HTTP status. `type` links to a page about the status. Quote `traceId` when you contact Spillard support.\n\n| Status | What to do |\n|---|---|\n| 429 and every 5xx except 501 (`retryable` is true) | Retry with exponential backoff. Wait for `Retry-After` when it is present. |\n| 401 | Get a new token. |\n| 409 `idempotency.in_progress` | Wait a few seconds, then send the same request again with the same `Idempotency-Key`. |\n| Every other 4xx, and 501 | Do not retry unchanged. Fix the request. |\n\n## Idempotency and concurrency\n\n- **Idempotency-Key.** Send a unique value (for example a UUID) on a create or another action that takes the header: a retry with the same key and body returns the first result instead of acting twice, and the same key with another body is 409 `idempotency.key_conflict`. Keys are kept for 24 hours per API client. Media requests require it (428 without).\n- **After a server error on a media request,** repeat it with the same key and body: the first call may have queued it. If the answer stays 409 `idempotency.in_progress`, look in `GET /v1/commands` for the request before you send it again with a new key.\n- **ETag and If-Match.** Vehicles, drivers and fleets carry an `ETag`. An update of a vehicle or a driver requires `If-Match` with the `ETag` of your last read: 428 without it, 412 when the resource changed since.\n\n## Rate limits\n\nEach API client may spend 3,000 units per minute unless agreed otherwise. A request costs 1 unit; the lists that read the most data cost 5 (`GET /v1/events`, `GET /v1/devices/{deviceId}/events`, `GET /v1/journeys` with its `/events` and `/frames`, `GET /v1/commands`, `GET /v1/metrics/human-detection-operational-time` and `GET /v1/vehicle-positions`), and `count=true` adds 5.\n\nResponses to authenticated requests carry `RateLimit-Policy` (the quota `q` in units per window of `w` seconds) and `RateLimit` (`r` units left, `t` seconds until the oldest request in the window stops counting), the IETF RateLimit header fields. Above the limit the answer is 429 `rate.limit_exceeded` with `Retry-After` (seconds).\n\n## Compression\n\nSend `Accept-Encoding: br, gzip`: JSON responses, errors included, then come compressed with Brotli or gzip. Responses that carry a secret or download links (`Cache-Control: no-store`) are never compressed. Send request bodies uncompressed.\n\n## Values that can grow\n\nRequests are validated against the listed values: an unknown value in a request body or a query parameter is 400 `validation.failed`. A response field that can gain values within v1 (event types, command types, states, scopes and the others) is a plain string whose description lists the values known today. Handle a value you do not know: keep it, and treat it as \"other\". The enum models of this reference list every value with its meaning.\n\n## Webhooks\n\nSpillard POSTs each new message as a CloudEvents 1.0 envelope, signed with Standard Webhooks headers (`webhook-id`, `webhook-timestamp`, `webhook-signature`), to your HTTPS endpoints. Create endpoints under Webhook endpoints, check them with a test message, then turn delivery on under Webhook settings. The Webhooks section of this reference describes every message, its retries and how to verify it.\n\n- `eventTypes` of an endpoint are webhook types (`spillard.*.v1`). `eventTypeFilters` narrow `spillard.event.raised.v1` to device event types such as `adas.*`.\n- We suggest you reject a message whose `webhook-timestamp` is more than 5 minutes from your clock.\n- To follow a video request, poll `GET /v1/media-requests/{mediaRequestId}` until `state` is `completed`, `failed`, `cancelled` or `timed_out`. You can also wait for the `spillard.media.request.completed.v1` webhook, but do not rely on it alone. Only `completed` is reliable today; `failed` and `no_data` are in future development, do not rely on them. A request that ends without an answer from the device may send no message.\n\n## Versioning\n\nv1 changes only additively: new operations, optional parameters, response fields and enumeration values. Ignore fields you do not know. A breaking change comes as a new version (`/v2`).",
    "contact": {
      "name": "Spillard Live",
      "url": "https://connect.spillard.live/"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://www.spillard.com/terms-and-conditions/"
    },
    "version": "1.0.0",
    "summary": "Fleet data, video and webhooks of Spillard Live for your own systems."
  },
  "servers": [
    {
      "url": "https://connect.spillard.live",
      "description": "Production."
    }
  ],
  "paths": {
    "/v1/api-clients": {
      "get": {
        "tags": [
          "ApiClients"
        ],
        "summary": "List API clients",
        "description": "Lists the API clients of the organizations your client may read whose access is no wider than your client's: every organization such a client reads is one your client reads, with every fleet where it reads every fleet. Oldest first. `access` shows what each client may read. Secrets are never returned.\n\n**Required scope:** `api-clients.read`",
        "operationId": "listApiClients",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/ManagementLimit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiClientPage"
                },
                "example": {
                  "items": [
                    {
                      "clientId": "sl-4f1c8e2a9b3d47c6a5e0d9b8c7f61a23",
                      "name": "Telematics export",
                      "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                      "scopes": [
                        "vehicles.read",
                        "events.read"
                      ],
                      "active": true,
                      "createdAt": "2026-09-29T08:12:00.000Z",
                      "secretHint": "q4Mv",
                      "previousSecretExpiresAt": null,
                      "access": [
                        {
                          "organization": {
                            "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                          },
                          "includeSubOrganizations": false,
                          "allFleets": true,
                          "fleets": [ ]
                        }
                      ]
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "api-clients.read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Create an API client",
        "description": "Creates an API client for server-to-server access and returns its secret, once.\n\n- **Access.** The client reads `organizationId` only (not the organizations below it). `organizationId` must be an organization your client reads with every fleet: one it reads for some fleets only is 403 `auth.fleet_restricted`, one outside your access 403 `auth.tenant_forbidden`. Clients with several organizations or limited to fleets are set up by Spillard.\n- **Scopes.** Chosen from your client's own; a scope your client does not hold is 403 `auth.scope_missing`.\n- **Secret.** Shown exactly once, in this response. Store it safely.\n- **Retry.** With an `Idempotency-Key`, a retry within 24 hours returns the same client with a new secret (the earlier one stays valid for 24 hours); a retry after the client has changed is 409 `idempotency.key_conflict` and changes nothing: send the create with a new key.\n\n**Required scope:** `api-clients.write`",
        "operationId": "createApiClient",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Optional. A unique value per logical request (for example a UUID). A retry with the same key and body within 24 hours returns the same client with a new secret (the earlier one stays valid for 24 hours); a retry after the client has changed is 409 `idempotency.key_conflict` and changes nothing.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApiClientRequest"
              },
              "example": {
                "name": "Telematics export",
                "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                "scopes": [
                  "vehicles.read",
                  "events.read"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiClientCreated"
                },
                "example": {
                  "clientId": "sl-4f1c8e2a9b3d47c6a5e0d9b8c7f61a23",
                  "clientSecret": "u7Qe2xV9kLm4Rt8ZpW1sYc6Hn3Bf5Jd0Ag2Ke9Tq4Mv",
                  "secretHint": "q4Mv",
                  "name": "Telematics export",
                  "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                  "scopes": [
                    "vehicles.read",
                    "events.read"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `idempotency.key_conflict`: The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "idempotency.key_conflict": {
                    "$ref": "#/components/examples/idempotency.key_conflict"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "api-clients.write"
            ]
          }
        ]
      }
    },
    "/v1/api-clients/{clientId}": {
      "get": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Get an API client",
        "description": "Returns one API client with its access and scopes, never its secret. A client your client may not see is 404 `api_client.not_found`.\n\n**Required scope:** `api-clients.read`",
        "operationId": "getApiClient",
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "description": "The API client's id: its OAuth2 client id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiClient"
                },
                "example": {
                  "clientId": "sl-4f1c8e2a9b3d47c6a5e0d9b8c7f61a23",
                  "name": "Telematics export",
                  "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                  "scopes": [
                    "vehicles.read",
                    "events.read"
                  ],
                  "active": true,
                  "createdAt": "2026-09-29T08:12:00.000Z",
                  "secretHint": "q4Mv",
                  "previousSecretExpiresAt": null,
                  "access": [
                    {
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "includeSubOrganizations": false,
                      "allFleets": true,
                      "fleets": [ ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `api_client.not_found`: The API client does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "api_client.not_found": {
                    "$ref": "#/components/examples/api_client.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "api-clients.read"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Update an API client",
        "description": "Renames the client or replaces its scopes; a field you leave out stays unchanged. The new scopes are chosen from your client's own (403 `auth.scope_missing` otherwise). Tokens already issued keep their scopes until they expire.\n\nA client whose access or scopes are wider than your client's is 404 `api_client.not_found`.\n\n**Required scope:** `api-clients.write`",
        "operationId": "updateApiClient",
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "description": "The API client's id: its OAuth2 client id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateApiClientRequest"
              },
              "example": {
                "scopes": [
                  "vehicles.read",
                  "events.read",
                  "journeys.read"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiClient"
                },
                "example": {
                  "clientId": "sl-4f1c8e2a9b3d47c6a5e0d9b8c7f61a23",
                  "name": "Telematics export",
                  "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                  "scopes": [
                    "vehicles.read",
                    "events.read",
                    "journeys.read"
                  ],
                  "active": true,
                  "createdAt": "2026-09-29T08:12:00.000Z",
                  "secretHint": "q4Mv",
                  "previousSecretExpiresAt": null,
                  "access": [
                    {
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "includeSubOrganizations": false,
                      "allFleets": true,
                      "fleets": [ ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `api_client.not_found`: The API client does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "api_client.not_found": {
                    "$ref": "#/components/examples/api_client.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "api-clients.write"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Delete an API client",
        "description": "Deletes the client (the Spillard web app calls this Revoke): it can no longer get tokens, with any of its secrets. Tokens already issued work until they expire. A client whose access or scopes are wider than your client's is 404 `api_client.not_found`.\n\n**Required scope:** `api-clients.write`",
        "operationId": "deleteApiClient",
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "description": "The API client's id: its OAuth2 client id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `api_client.not_found`: The API client does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "api_client.not_found": {
                    "$ref": "#/components/examples/api_client.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "api-clients.write"
            ]
          }
        ]
      }
    },
    "/v1/api-clients/{clientId}:rotate-secret": {
      "post": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Rotate an API client's secret",
        "description": "Issues a new client secret, shown exactly once.\n\n- The replaced secret keeps getting tokens until `previousSecretExpiresAt` (24 hours): switch your integration to the new secret within that time. Tokens already issued stay valid until they expire.\n- Rotate once, then switch. No `Idempotency-Key`: rotating again (for example after a lost response) replaces the secret again and ends the earlier secret's overlap at once, so only the latest secret and the one it replaced work.\n- Deleting the client stops every secret at once.\n- A client whose access or scopes are wider than your client's is 404 `api_client.not_found`.\n\n**Required scope:** `api-clients.write`",
        "operationId": "rotateApiClientSecret",
        "parameters": [
          {
            "name": "clientId",
            "in": "path",
            "description": "The API client's id: its OAuth2 client id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiClientSecretRotated"
                },
                "example": {
                  "clientSecret": "p3Lr8Wn1Ys6Cv0Qk5Hb9Tg2Mx7Df4Zj1Ua8Ne3Ro6Ks",
                  "secretHint": "o6Ks",
                  "previousSecretExpiresAt": "2026-09-30T08:20:00.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `api_client.not_found`: The API client does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "api_client.not_found": {
                    "$ref": "#/components/examples/api_client.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "api-clients.write"
            ]
          }
        ]
      }
    },
    "/v1/commands": {
      "get": {
        "tags": [
          "Commands"
        ],
        "summary": "List device commands",
        "description": "Lists the commands sent to devices (video requests, reboots and the other types), newest first.\n\n- `createdFrom`/`createdTo` bound the creation time. Without `createdFrom`, the 90 days before `createdTo` (now when `createdTo` is not given). The range spans at most 90 days, otherwise 400 `validation.failed`.\n- Every type is listed unless `types` narrows it: `types=video` lists the video requests only. An unknown `types` or `states` value is 400 `validation.failed`.\n- Commands are read-only in v1. Request a video clip with `POST /v1/media-requests`.\n\n**Required scope:** `commands.read`",
        "operationId": "listCommands",
        "parameters": [
          {
            "name": "hardwareIds",
            "in": "query",
            "description": "Only the data of the devices with these hardware ids (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "states",
            "in": "query",
            "description": "Only commands in one of these states (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/CommandState"
              }
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "Only the data of this organization and, unless `includeSubOrganizations=false`, of the organizations below it (sub-organizations) that your client may read. It must be an organization your client may read (see `access` in `GET /v1/context`; an organization below one granted with its sub-organizations counts), otherwise 403 `auth.tenant_forbidden`. For an organization your client may read for some fleets only, only those fleets' data is returned.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "includeSubOrganizations",
            "in": "query",
            "description": "Whether sub-organizations are included: the organizations below `organizationId` (or, without it, below the organizations your client was granted); default true. With `organizationId`: true returns that organization and the organizations below it that your client may read, false that organization only. Without `organizationId`: true returns everything your client may read, false only the organizations your client was granted, without the organizations below them. It never widens what your client may read.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "fleetIds",
            "in": "query",
            "description": "Only the data of these fleets (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "vehicleIds",
            "in": "query",
            "description": "Only commands of these vehicles (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "driverIds",
            "in": "query",
            "description": "Only commands of these drivers (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "createdFrom",
            "in": "query",
            "description": "Only commands created at or after this instant (UTC, ISO 8601). Without `createdFrom`, the 90 days before `createdTo`. The range spans at most 90 days (400 `validation.failed` otherwise).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "createdTo",
            "in": "query",
            "description": "Only commands created before this instant (UTC, ISO 8601); now when not given.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "types",
            "in": "query",
            "description": "Only commands of one of these types (repeat the parameter for several, at most 100 values). `types=video` lists the video requests only.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/CommandType"
              }
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandPage"
                },
                "example": {
                  "items": [
                    {
                      "id": "9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683",
                      "state": "completed",
                      "type": "video",
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                      },
                      "device": {
                        "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                        "hardwareId": "009a2c41f7"
                      },
                      "event": {
                        "id": "1f3b8d27-6c4e-4a95-b0d2-7e9a5c1f3b68"
                      },
                      "createdAt": "2026-09-29T08:05:12.000Z",
                      "updatedAt": "2026-09-29T08:07:48.000Z"
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "commands.read"
            ]
          }
        ]
      }
    },
    "/v1/commands/{commandId}": {
      "get": {
        "tags": [
          "Commands"
        ],
        "summary": "Get a device command",
        "description": "Returns one command with its payload and state. A command your client may not read is 404 `command.not_found`.\n\n**Required scope:** `commands.read`",
        "operationId": "getCommand",
        "parameters": [
          {
            "name": "commandId",
            "in": "path",
            "description": "The command's id, as `id` in a command response. An opaque id: treat it as text of up to 255 characters; do not parse it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Command"
                },
                "example": {
                  "id": "9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683",
                  "state": "completed",
                  "type": "video",
                  "manual": true,
                  "createdBy": null,
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                  },
                  "driver": {
                    "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                  },
                  "device": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "event": {
                    "id": "1f3b8d27-6c4e-4a95-b0d2-7e9a5c1f3b68"
                  },
                  "payload": {
                    "type": "video",
                    "channels": [
                      {
                        "number": 1,
                        "name": "Road facing"
                      },
                      {
                        "number": 2,
                        "name": "Driver facing"
                      }
                    ],
                    "startAt": "2026-09-29T07:40:00.000Z",
                    "durationSeconds": 30,
                    "videoQuality": "hd",
                    "overlay": true
                  },
                  "createdAt": "2026-09-29T08:05:12.000Z",
                  "updatedAt": "2026-09-29T08:07:48.000Z",
                  "failureReason": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `command.not_found`: The command or media request does not exist or is outside your client's access. Check the id. The media request routes answer with this code too.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "command.not_found": {
                    "$ref": "#/components/examples/command.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "commands.read"
            ]
          }
        ]
      }
    },
    "/v1/context": {
      "get": {
        "tags": [
          "Context"
        ],
        "summary": "Get your client's access",
        "description": "Shows how the API sees your token: the client id, your client's organization and its name, the access it has now (`access`: each organization it was granted, with or without the organizations below it (sub-organizations), with every fleet or limited to the listed fleets), its licenses and its scopes.\n\n- `dataAccessDays` is 30 when your organization's data access is limited to the last 30 days, otherwise null.\n- Call it first to check a new client. It needs a valid token but no scope.",
        "operationId": "getContext",
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthContext"
                },
                "example": {
                  "clientId": "sl-4f1c8e2a9b3d47c6a5e0d9b8c7f61a23",
                  "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                  "organizationName": "Spillard Logistics",
                  "licenses": [
                    "Video"
                  ],
                  "scopes": [
                    "events.read",
                    "vehicles.read"
                  ],
                  "dataAccessDays": null,
                  "access": [
                    {
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "includeSubOrganizations": true,
                      "allFleets": true,
                      "fleets": [ ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token's access list is damaged (`auth.tenant_forbidden`); ask Spillard to check the API client.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [ ]
          }
        ]
      }
    },
    "/v1/device-models": {
      "get": {
        "tags": [
          "DeviceCatalog"
        ],
        "summary": "List device models",
        "description": "Lists the device models Spillard supports: reference data, the same for every organization. Disabled models are not listed. Ordered by `id`; 100 per page by default, at most 1,000 (`limit`); follow `nextCursor`.\n\n**Required scope:** `devices.read`",
        "operationId": "listDeviceModels",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceModelPage"
                },
                "example": {
                  "items": [
                    {
                      "id": "b7e3a1c9-5d2f-4e86-9c0a-1f4d7b2e6a58",
                      "name": "ME41"
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "devices.read"
            ]
          }
        ]
      }
    },
    "/v1/device-models/{deviceModelId}": {
      "get": {
        "tags": [
          "DeviceCatalog"
        ],
        "summary": "Get a device model",
        "description": "Returns one device model. An unknown id, or a disabled model, is 404 `device_model.not_found`.\n\n**Required scope:** `devices.read`",
        "operationId": "getDeviceModel",
        "parameters": [
          {
            "name": "deviceModelId",
            "in": "path",
            "description": "The device model's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceModel"
                },
                "example": {
                  "id": "b7e3a1c9-5d2f-4e86-9c0a-1f4d7b2e6a58",
                  "name": "ME41"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device_model.not_found`: No device model has this id. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device_model.not_found": {
                    "$ref": "#/components/examples/device_model.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "devices.read"
            ]
          }
        ]
      }
    },
    "/v1/device-protocols": {
      "get": {
        "tags": [
          "DeviceCatalog"
        ],
        "summary": "List device protocols",
        "description": "Lists the communication protocols of the devices Spillard supports: reference data, the same for every organization. Disabled protocols are not listed. Ordered by `id`; 100 per page by default, at most 1,000 (`limit`); follow `nextCursor`.\n\n**Required scope:** `devices.read`",
        "operationId": "listDeviceProtocols",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceProtocolPage"
                },
                "example": {
                  "items": [
                    {
                      "id": "d1f6c8a2-3b7e-4c59-8e0d-9a2b5f7c4e16",
                      "name": "Howen"
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "devices.read"
            ]
          }
        ]
      }
    },
    "/v1/device-protocols/{deviceProtocolId}": {
      "get": {
        "tags": [
          "DeviceCatalog"
        ],
        "summary": "Get a device protocol",
        "description": "Returns one device protocol. An unknown id, or a disabled protocol, is 404 `device_protocol.not_found`.\n\n**Required scope:** `devices.read`",
        "operationId": "getDeviceProtocol",
        "parameters": [
          {
            "name": "deviceProtocolId",
            "in": "path",
            "description": "The device protocol's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceProtocol"
                },
                "example": {
                  "id": "d1f6c8a2-3b7e-4c59-8e0d-9a2b5f7c4e16",
                  "name": "Howen"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device_protocol.not_found`: No device protocol has this id. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device_protocol.not_found": {
                    "$ref": "#/components/examples/device_protocol.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "devices.read"
            ]
          }
        ]
      }
    },
    "/v1/devices": {
      "get": {
        "tags": [
          "Devices"
        ],
        "summary": "List devices",
        "description": "Lists the tracking and camera devices your client may read, with their fleet, vehicle, model, protocol, channels and connectivity.\n\n- The filters combine: a device must match all of them.\n- Ordered by device `id`. 100 devices per page by default, at most 1,000 (`limit`); follow `nextCursor`.\n- A list filtered by `connectivityStatus` or `lastReportedFrom` is 503 `dependency.unavailable` while live connectivity cannot be read. `count=true` is 400 `validation.failed` with those filters.\n\n**Required scope:** `devices.read`",
        "operationId": "listDevices",
        "parameters": [
          {
            "name": "fleetIds",
            "in": "query",
            "description": "Only the data of these fleets (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "Only the data of this organization and, unless `includeSubOrganizations=false`, of the organizations below it (sub-organizations) that your client may read. It must be an organization your client may read (see `access` in `GET /v1/context`; an organization below one granted with its sub-organizations counts), otherwise 403 `auth.tenant_forbidden`. For an organization your client may read for some fleets only, only those fleets' data is returned.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "organizationIds",
            "in": "query",
            "description": "Only the data of these organizations. Ids outside the organizations your client may read match nothing (never a 403). For an organization your client may read for some fleets only, only those fleets' data is returned. Applied after `organizationId` and `includeSubOrganizations`: it narrows their result and never adds the organizations below an id.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "includeSubOrganizations",
            "in": "query",
            "description": "Whether sub-organizations are included: the organizations below `organizationId` (or, without it, below the organizations your client was granted); default true. With `organizationId`: true returns that organization and the organizations below it that your client may read, false that organization only. Without `organizationId`: true returns everything your client may read, false only the organizations your client was granted, without the organizations below them. It never widens what your client may read. `organizationIds` applies afterwards: it narrows the result to the organizations it lists and never adds the organizations below an id.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "hardwareIds",
            "in": "query",
            "description": "Only the data of the devices with these hardware ids (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "simNumber",
            "in": "query",
            "description": "Only the device with exactly this SIM number.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "serialNumber",
            "in": "query",
            "description": "Only the device with exactly this serial number.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "vehicleAssigned",
            "in": "query",
            "description": "`true`: only devices fitted to a vehicle. `false`: only devices without a vehicle.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "fleetAssigned",
            "in": "query",
            "description": "`true`: only devices in a fleet. `false`: only devices without a fleet.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "vehicleIds",
            "in": "query",
            "description": "Only devices fitted to one of these vehicles.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Only devices whose hardware id or SIM number, or the registration number of their vehicle, contains this text (case-insensitive; at most 100 characters). `hardwareIds`, `simNumber` and `serialNumber` match exactly.",
            "schema": {
              "maxLength": 100,
              "type": "string"
            }
          },
          {
            "name": "connectivityStatus",
            "in": "query",
            "description": "Only devices with one of these connectivity statuses (repeat the parameter for several, at most 100 values). Connectivity comes from live data: a filtered list is 503 `dependency.unavailable` while that data cannot be read. A filtered page may hold fewer items than `limit` while `hasMore` is true; follow `nextCursor`. `count=true` is 400 `validation.failed` with this filter.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/ConnectivityStatus"
              }
            }
          },
          {
            "name": "lastReportedFrom",
            "in": "query",
            "description": "Only devices that last reported at or after this instant (UTC, ISO 8601). A device that never reported never matches. Uses the same live data as `connectivityStatus`. `count=true` is 400 `validation.failed` with it.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DevicePage"
                },
                "example": {
                  "items": [
                    {
                      "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                      "hardwareId": "009a2c41f7",
                      "enabled": true,
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                      },
                      "model": {
                        "id": "b7e3a1c9-5d2f-4e86-9c0a-1f4d7b2e6a58",
                        "name": "ME41"
                      },
                      "protocol": {
                        "id": "d1f6c8a2-3b7e-4c59-8e0d-9a2b5f7c4e16",
                        "name": "Howen"
                      },
                      "firmwareVersion": "4.2.17",
                      "simNumber": "8944100000123456789",
                      "serialNumber": "ME41-2207-0193",
                      "channels": [
                        {
                          "number": 1,
                          "name": "Road facing"
                        },
                        {
                          "number": 2,
                          "name": "Driver facing"
                        }
                      ],
                      "connectivityStatus": "online",
                      "lastReportedAt": "2026-09-29T08:02:40.000Z",
                      "firstReportedAt": "2025-03-14T09:12:05.000Z"
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "devices.read"
            ]
          }
        ]
      }
    },
    "/v1/devices/{deviceId}": {
      "get": {
        "tags": [
          "Devices"
        ],
        "summary": "Get a device",
        "description": "Returns one device. `{deviceId}` is the device's id or its hardware id. A device your client may not read is 404 `device.not_found`.\n\n**Required scope:** `devices.read`",
        "operationId": "getDevice",
        "parameters": [
          {
            "name": "deviceId",
            "in": "path",
            "description": "The device's id (a UUID) or its hardware id. A UUID is looked up as a device id first; a value that is not the id of one of your devices is looked up as a hardware id (case-insensitive).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Device"
                },
                "example": {
                  "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                  "hardwareId": "009a2c41f7",
                  "enabled": true,
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                  },
                  "model": {
                    "id": "b7e3a1c9-5d2f-4e86-9c0a-1f4d7b2e6a58",
                    "name": "ME41"
                  },
                  "protocol": {
                    "id": "d1f6c8a2-3b7e-4c59-8e0d-9a2b5f7c4e16",
                    "name": "Howen"
                  },
                  "firmwareVersion": "4.2.17",
                  "simNumber": "8944100000123456789",
                  "serialNumber": "ME41-2207-0193",
                  "channels": [
                    {
                      "number": 1,
                      "name": "Road facing"
                    },
                    {
                      "number": 2,
                      "name": "Driver facing"
                    }
                  ],
                  "connectivityStatus": "online",
                  "lastReportedAt": "2026-09-29T08:02:40.000Z",
                  "firstReportedAt": "2025-03-14T09:12:05.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device.not_found`: The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device.not_found": {
                    "$ref": "#/components/examples/device.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "devices.read"
            ]
          }
        ]
      }
    },
    "/v1/devices/{deviceId}/events": {
      "get": {
        "tags": [
          "Devices"
        ],
        "summary": "List a device's events",
        "description": "Lists the events of one device, newest trigger time first by default. The time range, `order`, `view` and paging work as in `GET /v1/events`: both ranges apply when both are sent, each spans at most 92 days (400 `validation.failed`), requested events are listed and unclassified ones are not. A device your client may not read is 404 `device.not_found`.\n\nAn organization whose data access is limited to 30 days (`dataAccessDays` in `GET /v1/context`) gets no event triggered before then; a page may then hold fewer items than `limit` while `hasMore` is true.\n\n- `mediaStatus` is `available` (media arrived), `pending` (a media request for the event is still open) or `none`.\n- `include=shareUrl` adds `share` to each event: a public link to its page in the Spillard web app, valid for 30 days (until the event is 30 days old, for an organization limited to 30 days). It needs the `media.read` scope too (403 `auth.scope_missing` without it). The link cannot be revoked before it expires, so treat it as a secret. `share` is null for an event whose fleet, or its device's fleet, requires a video audit form, and for an event whose page would show data outside your client's access (its device has left that access, or some of its media lie outside it).\n\n**Required scope:** `events.read`",
        "operationId": "listDeviceEvents",
        "parameters": [
          {
            "name": "deviceId",
            "in": "path",
            "description": "The device's id (a UUID) or its hardware id. A UUID is looked up as a device id first; a value that is not the id of one of your devices is looked up as a hardware id (case-insensitive).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Only events triggered at or after this instant (UTC, ISO 8601). Both windows apply when both are sent; each may span at most 92 days (longer is 400 `validation.failed`). Without any window, the last 24 hours apply.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Only events triggered at or before this instant (UTC, ISO 8601); now when only `from` is given.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "receivedFrom",
            "in": "query",
            "description": "Only events Spillard received at or after this instant (UTC, ISO 8601). Both windows apply when both are sent; each may span at most 92 days (longer is 400 `validation.failed`). Without any window, the last 24 hours apply.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "receivedTo",
            "in": "query",
            "description": "Only events Spillard received at or before this instant (UTC, ISO 8601); now when only `receivedFrom` is given.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "order",
            "in": "query",
            "description": "The order of the list: newest trigger time first by default, newest received time first when only a received window is given. Events of the same second come in a fixed order that is not always their order within that second. `received_at_asc` suits an incremental sync. A `cursor` belongs to the order it was issued for.",
            "schema": {
              "$ref": "#/components/schemas/EventOrder"
            }
          },
          {
            "name": "view",
            "in": "query",
            "description": "`basic` leaves out the per-sample arrays (`gSensorFrames`, `gyroscopeFrames`, `speedPointsKph`, `headingPointsDegrees`) for a cheap synchronization; `full` (the default) returns every field.",
            "schema": {
              "$ref": "#/components/schemas/EventView"
            }
          },
          {
            "name": "include",
            "in": "query",
            "description": "What to add to each event: `shareUrl` adds `share`, a public link to the event's page in the Spillard web app (working for 30 days; it shows the event and its media without a sign-in). `shareUrl` needs the `media.read` scope too: 403 `auth.scope_missing` without it.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/EventInclude"
              }
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/EventLimit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceEventPage"
                },
                "example": {
                  "items": [
                    {
                      "id": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                      "triggeredAt": "2026-09-29T07:41:12.000Z",
                      "receivedAt": "2026-09-29T07:41:15.000Z",
                      "eventTypes": [
                        "driver_behaviour.harsh_braking"
                      ],
                      "categories": [
                        "driver_behaviour"
                      ],
                      "classification": "high",
                      "origin": "device",
                      "vendorEventType": "HARSH_BRAKE",
                      "firmwareVersion": "4.2.17",
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                        "registrationNumber": "SL24 WHK"
                      },
                      "driver": {
                        "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                      },
                      "device": {
                        "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                        "hardwareId": "009a2c41f7"
                      },
                      "locations": [
                        {
                          "latitude": 51.518392,
                          "longitude": -0.281046
                        }
                      ],
                      "address": {
                        "label": "Western Avenue (A40), Ealing, London, United Kingdom",
                        "houseNumber": null,
                        "street": "Western Avenue",
                        "district": "Ealing",
                        "city": "London",
                        "county": "Greater London",
                        "state": "England",
                        "postalCode": "W3 0TA",
                        "country": "United Kingdom"
                      },
                      "speedKph": 52,
                      "maxSpeedKph": 61,
                      "speedLimitKph": 64,
                      "speedPointsKph": [
                        61,
                        52,
                        31
                      ],
                      "headingPointsDegrees": [
                        92,
                        92,
                        91
                      ],
                      "gSensorFrames": [
                        {
                          "sequenceNumber": 0,
                          "sampledAt": "2026-09-29T07:41:12.000Z",
                          "xAxis": 0.02,
                          "yAxis": -0.61,
                          "zAxis": 1.01
                        }
                      ],
                      "gyroscopeFrames": null,
                      "hasMedia": true,
                      "mediaStatus": "available",
                      "share": null
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null,
                  "meta": {
                    "effectiveFrom": "2026-09-28T08:05:00.000Z",
                    "effectiveTo": "2026-09-29T08:05:00.000Z",
                    "effectiveReceivedFrom": null,
                    "effectiveReceivedTo": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device.not_found`: The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device.not_found": {
                    "$ref": "#/components/examples/device.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "events.read"
            ]
          }
        ]
      }
    },
    "/v1/devices/{deviceId}/position": {
      "get": {
        "tags": [
          "Devices"
        ],
        "summary": "Get a device's latest position",
        "description": "Returns the latest position the device reported. The answer is 204 with no body when the device has not reported a position yet or does not track its position. A device your client may not read is 404 `device.not_found`.\n\n**Required scope:** `devices.read`",
        "operationId": "getDevicePosition",
        "parameters": [
          {
            "name": "deviceId",
            "in": "path",
            "description": "The device's id (a UUID) or its hardware id. A UUID is looked up as a device id first; a value that is not the id of one of your devices is looked up as a hardware id (case-insensitive).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DevicePosition"
                },
                "example": {
                  "device": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                  },
                  "driver": {
                    "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                  },
                  "location": {
                    "latitude": 51.481022,
                    "longitude": -0.429587
                  },
                  "gpsValid": true,
                  "speedKph": 0,
                  "headingDegrees": 270,
                  "recordedAt": "2026-09-29T08:02:40.000Z"
                }
              }
            }
          },
          "204": {
            "description": "No Content",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device.not_found`: The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device.not_found": {
                    "$ref": "#/components/examples/device.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "devices.read"
            ]
          }
        ]
      }
    },
    "/v1/devices/{deviceId}/events/{eventId}": {
      "get": {
        "tags": [
          "Devices"
        ],
        "summary": "Get an event of one device",
        "description": "Returns one event of the device: like `GET /v1/events/{eventId}`, but an event of another device is 404 `event.not_found`. A device your client may not read is 404 `device.not_found`.\n\nAn event older than its organization's data access (`dataAccessDays` in `GET /v1/context`) is 403 `data.retention_restricted`.\n\n- `mediaStatus` is `available` (media arrived), `pending` (a media request for the event is still open) or `none`.\n- `include=shareUrl` adds `share` to each event: a public link to its page in the Spillard web app, valid for 30 days (until the event is 30 days old, for an organization limited to 30 days). It needs the `media.read` scope too (403 `auth.scope_missing` without it). The link cannot be revoked before it expires, so treat it as a secret. `share` is null for an event whose fleet, or its device's fleet, requires a video audit form, and for an event whose page would show data outside your client's access (its device has left that access, or some of its media lie outside it).\n\n**Required scope:** `events.read`",
        "operationId": "getDeviceEvent",
        "parameters": [
          {
            "name": "deviceId",
            "in": "path",
            "description": "The device's id (a UUID) or its hardware id. A UUID is looked up as a device id first; a value that is not the id of one of your devices is looked up as a hardware id (case-insensitive).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "eventId",
            "in": "path",
            "description": "The event's id, as `id` in an event response. An opaque id: treat it as text of up to 255 characters; do not parse it. An event id as base64 text of `{id},{hardwareId}` (also URL-safe or percent-encoded) is accepted too; responses never carry that form.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include",
            "in": "query",
            "description": "What to add to each event: `shareUrl` adds `share`, a public link to the event's page in the Spillard web app (working for 30 days; it shows the event and its media without a sign-in). `shareUrl` needs the `media.read` scope too: 403 `auth.scope_missing` without it.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/EventInclude"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceEvent"
                },
                "example": {
                  "id": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                  "triggeredAt": "2026-09-29T07:41:12.000Z",
                  "receivedAt": "2026-09-29T07:41:15.000Z",
                  "eventTypes": [
                    "driver_behaviour.harsh_braking"
                  ],
                  "categories": [
                    "driver_behaviour"
                  ],
                  "classification": "high",
                  "origin": "device",
                  "vendorEventType": "HARSH_BRAKE",
                  "firmwareVersion": "4.2.17",
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                    "registrationNumber": "SL24 WHK"
                  },
                  "driver": {
                    "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                  },
                  "device": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "locations": [
                    {
                      "latitude": 51.518392,
                      "longitude": -0.281046
                    }
                  ],
                  "address": {
                    "label": "Western Avenue (A40), Ealing, London, United Kingdom",
                    "houseNumber": null,
                    "street": "Western Avenue",
                    "district": "Ealing",
                    "city": "London",
                    "county": "Greater London",
                    "state": "England",
                    "postalCode": "W3 0TA",
                    "country": "United Kingdom"
                  },
                  "speedKph": 52,
                  "maxSpeedKph": 61,
                  "speedLimitKph": 64,
                  "speedPointsKph": [
                    61,
                    52,
                    31
                  ],
                  "headingPointsDegrees": [
                    92,
                    92,
                    91
                  ],
                  "gSensorFrames": [
                    {
                      "sequenceNumber": 0,
                      "sampledAt": "2026-09-29T07:41:12.000Z",
                      "xAxis": 0.02,
                      "yAxis": -0.61,
                      "zAxis": 1.01
                    }
                  ],
                  "gyroscopeFrames": null,
                  "hasMedia": true,
                  "mediaStatus": "available",
                  "share": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `data.retention_restricted`: The data is older than the organization's data access allows (see `dataAccessDays` in `GET /v1/context`). Ask for newer data.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "data.retention_restricted": {
                    "$ref": "#/components/examples/data.retention_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device.not_found`: The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.\n- `event.not_found`: The event does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device.not_found": {
                    "$ref": "#/components/examples/device.not_found"
                  },
                  "event.not_found": {
                    "$ref": "#/components/examples/event.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "events.read"
            ]
          }
        ]
      }
    },
    "/v1/devices/{deviceId}:move-fleet": {
      "post": {
        "tags": [
          "Devices"
        ],
        "summary": "Move a device to another fleet",
        "description": "Moves the device to the fleet `fleetId` of the same organization and returns it; the device keeps its vehicle. A device already in that fleet is returned unchanged.\n\n- A fleet that does not exist or that your client may not read is 404 `fleet.not_found`; a device your client may not read is 404 `device.not_found`.\n- A fleet of another organization is 409 `resource.link_conflict`.\n- `Idempotency-Key` is optional.\n\n**Required scope:** `devices.write`",
        "operationId": "moveDeviceFleet",
        "parameters": [
          {
            "name": "deviceId",
            "in": "path",
            "description": "The device's id (a UUID) or its hardware id. A UUID is looked up as a device id first; a value that is not the id of one of your devices is looked up as a hardware id (case-insensitive).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Optional. A unique value per logical request (for example a UUID). A retry with the same key and body within 24 hours returns the first result instead of acting twice; the same key with another body is 409 `idempotency.key_conflict`. While the first request is still running the answer is 409 `idempotency.in_progress`: wait, then repeat the same request with the same key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoveDeviceFleetRequest"
              },
              "example": {
                "fleetId": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Device"
                },
                "example": {
                  "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                  "hardwareId": "009a2c41f7",
                  "enabled": true,
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                  },
                  "model": {
                    "id": "b7e3a1c9-5d2f-4e86-9c0a-1f4d7b2e6a58",
                    "name": "ME41"
                  },
                  "protocol": {
                    "id": "d1f6c8a2-3b7e-4c59-8e0d-9a2b5f7c4e16",
                    "name": "Howen"
                  },
                  "firmwareVersion": "4.2.17",
                  "simNumber": "8944100000123456789",
                  "serialNumber": "ME41-2207-0193",
                  "channels": [
                    {
                      "number": 1,
                      "name": "Road facing"
                    },
                    {
                      "number": 2,
                      "name": "Driver facing"
                    }
                  ],
                  "connectivityStatus": "online",
                  "lastReportedAt": "2026-09-29T08:02:40.000Z",
                  "firstReportedAt": "2025-03-14T09:12:05.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device.not_found`: The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.\n- `fleet.not_found`: The fleet does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device.not_found": {
                    "$ref": "#/components/examples/device.not_found"
                  },
                  "fleet.not_found": {
                    "$ref": "#/components/examples/fleet.not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `idempotency.in_progress`: The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.\n- `idempotency.key_conflict`: The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.\n- `resource.link_conflict`: The link cannot be made as asked: the vehicle already has a driver, the driver or device is linked to another vehicle, the two are in different organizations or fleets, or the registration number is taken in the target organization. Unassign the other link or change the target first.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "idempotency.in_progress": {
                    "$ref": "#/components/examples/idempotency.in_progress"
                  },
                  "idempotency.key_conflict": {
                    "$ref": "#/components/examples/idempotency.key_conflict"
                  },
                  "resource.link_conflict": {
                    "$ref": "#/components/examples/resource.link_conflict"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "devices.write"
            ]
          }
        ]
      }
    },
    "/v1/devices/{deviceId}:disable-ingest": {
      "post": {
        "tags": [
          "Devices"
        ],
        "summary": "Stop accepting data from a device",
        "description": "Sets the device's `enabled` flag to false, the same switch as in the Spillard web app: Spillard stops accepting the device's data. The device keeps its fleet and vehicle. Undo it with `:enable-ingest`; calling it again changes nothing. `Idempotency-Key` is optional.\n\n**Required scope:** `devices.write`",
        "operationId": "disableDeviceIngest",
        "parameters": [
          {
            "name": "deviceId",
            "in": "path",
            "description": "The device's id (a UUID) or its hardware id. A UUID is looked up as a device id first; a value that is not the id of one of your devices is looked up as a hardware id (case-insensitive).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Optional. A unique value per logical request (for example a UUID). A retry with the same key and body within 24 hours returns the first result instead of acting twice; the same key with another body is 409 `idempotency.key_conflict`. While the first request is still running the answer is 409 `idempotency.in_progress`: wait, then repeat the same request with the same key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Device"
                },
                "example": {
                  "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                  "hardwareId": "009a2c41f7",
                  "enabled": false,
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                  },
                  "model": {
                    "id": "b7e3a1c9-5d2f-4e86-9c0a-1f4d7b2e6a58",
                    "name": "ME41"
                  },
                  "protocol": {
                    "id": "d1f6c8a2-3b7e-4c59-8e0d-9a2b5f7c4e16",
                    "name": "Howen"
                  },
                  "firmwareVersion": "4.2.17",
                  "simNumber": "8944100000123456789",
                  "serialNumber": "ME41-2207-0193",
                  "channels": [
                    {
                      "number": 1,
                      "name": "Road facing"
                    },
                    {
                      "number": 2,
                      "name": "Driver facing"
                    }
                  ],
                  "connectivityStatus": "online",
                  "lastReportedAt": "2026-09-29T08:02:40.000Z",
                  "firstReportedAt": "2025-03-14T09:12:05.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device.not_found`: The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device.not_found": {
                    "$ref": "#/components/examples/device.not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `idempotency.in_progress`: The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.\n- `idempotency.key_conflict`: The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "idempotency.in_progress": {
                    "$ref": "#/components/examples/idempotency.in_progress"
                  },
                  "idempotency.key_conflict": {
                    "$ref": "#/components/examples/idempotency.key_conflict"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "devices.write"
            ]
          }
        ]
      }
    },
    "/v1/devices/{deviceId}:enable-ingest": {
      "post": {
        "tags": [
          "Devices"
        ],
        "summary": "Resume accepting data from a device",
        "description": "Sets the device's `enabled` flag back to true, so Spillard accepts its data again. Calling it again changes nothing. `Idempotency-Key` is optional.\n\n**Required scope:** `devices.write`",
        "operationId": "enableDeviceIngest",
        "parameters": [
          {
            "name": "deviceId",
            "in": "path",
            "description": "The device's id (a UUID) or its hardware id. A UUID is looked up as a device id first; a value that is not the id of one of your devices is looked up as a hardware id (case-insensitive).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Optional. A unique value per logical request (for example a UUID). A retry with the same key and body within 24 hours returns the first result instead of acting twice; the same key with another body is 409 `idempotency.key_conflict`. While the first request is still running the answer is 409 `idempotency.in_progress`: wait, then repeat the same request with the same key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Device"
                },
                "example": {
                  "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                  "hardwareId": "009a2c41f7",
                  "enabled": true,
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                  },
                  "model": {
                    "id": "b7e3a1c9-5d2f-4e86-9c0a-1f4d7b2e6a58",
                    "name": "ME41"
                  },
                  "protocol": {
                    "id": "d1f6c8a2-3b7e-4c59-8e0d-9a2b5f7c4e16",
                    "name": "Howen"
                  },
                  "firmwareVersion": "4.2.17",
                  "simNumber": "8944100000123456789",
                  "serialNumber": "ME41-2207-0193",
                  "channels": [
                    {
                      "number": 1,
                      "name": "Road facing"
                    },
                    {
                      "number": 2,
                      "name": "Driver facing"
                    }
                  ],
                  "connectivityStatus": "online",
                  "lastReportedAt": "2026-09-29T08:02:40.000Z",
                  "firstReportedAt": "2025-03-14T09:12:05.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device.not_found`: The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device.not_found": {
                    "$ref": "#/components/examples/device.not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `idempotency.in_progress`: The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.\n- `idempotency.key_conflict`: The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "idempotency.in_progress": {
                    "$ref": "#/components/examples/idempotency.in_progress"
                  },
                  "idempotency.key_conflict": {
                    "$ref": "#/components/examples/idempotency.key_conflict"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "devices.write"
            ]
          }
        ]
      }
    },
    "/v1/drivers": {
      "get": {
        "tags": [
          "Drivers"
        ],
        "summary": "List drivers",
        "description": "Lists the drivers your client may read, ordered by `id`. `fleetIds` narrows the list to those fleets; `name` matches part of the driver's full name.\n\n**Required scope:** `drivers.read`",
        "operationId": "listDrivers",
        "parameters": [
          {
            "name": "organizationId",
            "in": "query",
            "description": "Only the data of this organization and, unless `includeSubOrganizations=false`, of the organizations below it (sub-organizations) that your client may read. It must be an organization your client may read (see `access` in `GET /v1/context`; an organization below one granted with its sub-organizations counts), otherwise 403 `auth.tenant_forbidden`. For an organization your client may read for some fleets only, only those fleets' data is returned.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "includeSubOrganizations",
            "in": "query",
            "description": "Whether sub-organizations are included: the organizations below `organizationId` (or, without it, below the organizations your client was granted); default true. With `organizationId`: true returns that organization and the organizations below it that your client may read, false that organization only. Without `organizationId`: true returns everything your client may read, false only the organizations your client was granted, without the organizations below them. It never widens what your client may read.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "fleetIds",
            "in": "query",
            "description": "Only the data of these fleets (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "name",
            "in": "query",
            "description": "Only drivers whose full name contains this text.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DriverPage"
                },
                "example": {
                  "items": [
                    {
                      "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945",
                      "firstName": "Amira",
                      "lastName": "Khan",
                      "name": "Amira Khan",
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      }
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "drivers.read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Drivers"
        ],
        "summary": "Create a driver",
        "description": "Creates a driver in `fleetId` and returns it with its `ETag`.\n\n- `firstName`, `lastName` and `phoneNumber` are required, at most 50 characters each. An invalid field is 400 `validation.failed`; `errors` names each one.\n- A `fleetId` that does not exist or that your client may not read is 422 `validation.reference_not_found`.\n- Send an `Idempotency-Key` to retry safely: within 24 hours a retry with the same key and body returns the first result.\n\n**Required scope:** `drivers.write`",
        "operationId": "createDriver",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Optional. A unique value per logical request (for example a UUID). A retry with the same key and body within 24 hours returns the first result instead of acting twice; the same key with another body is 409 `idempotency.key_conflict`. While the first request is still running the answer is 409 `idempotency.in_progress`: wait, then repeat the same request with the same key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDriverRequest"
              },
              "example": {
                "firstName": "Amira",
                "lastName": "Khan",
                "phoneNumber": "+44 20 7946 0958",
                "fleetId": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "ETag": {
                "description": "The version of the resource. Send it back in `If-Match` when you update the resource.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Driver"
                },
                "example": {
                  "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945",
                  "firstName": "Amira",
                  "lastName": "Khan",
                  "name": "Amira Khan",
                  "phoneNumber": "+44 20 7946 0958",
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "currentVehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `idempotency.in_progress`: The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.\n- `idempotency.key_conflict`: The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "idempotency.in_progress": {
                    "$ref": "#/components/examples/idempotency.in_progress"
                  },
                  "idempotency.key_conflict": {
                    "$ref": "#/components/examples/idempotency.key_conflict"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Entity. `errorCode`:\n\n- `validation.reference_not_found`: An id in the body (for example `fleetId`) does not exist or is outside your client's access. Send an id your client may read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.reference_not_found": {
                    "$ref": "#/components/examples/validation.reference_not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "drivers.write"
            ]
          }
        ]
      }
    },
    "/v1/drivers/{driverId}": {
      "get": {
        "tags": [
          "Drivers"
        ],
        "summary": "Get a driver",
        "description": "Returns the driver with the vehicle they drive (`currentVehicle`), and its `ETag`.\n\n**Required scope:** `drivers.read`",
        "operationId": "getDriver",
        "parameters": [
          {
            "name": "driverId",
            "in": "path",
            "description": "The driver's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "ETag": {
                "description": "The version of the resource. Send it back in `If-Match` when you update the resource.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Driver"
                },
                "example": {
                  "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945",
                  "firstName": "Amira",
                  "lastName": "Khan",
                  "name": "Amira Khan",
                  "phoneNumber": "+44 20 7946 0958",
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "currentVehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `driver.not_found`: The driver does not exist or is outside your client's access. Check the id. On a vehicle route it can also mean the driver does not drive that vehicle.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "driver.not_found": {
                    "$ref": "#/components/examples/driver.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "drivers.read"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Drivers"
        ],
        "summary": "Update a driver",
        "description": "Changes only the fields you send; a required text cannot be blanked. Returns the driver with its new `ETag`. Send `If-Match` with the `ETag` of your last read: 428 `precondition.required` without it, 412 `concurrency.etag_mismatch` when the driver changed since. Read it again, then retry.\n\n**Required scope:** `drivers.write`",
        "operationId": "updateDriver",
        "parameters": [
          {
            "name": "driverId",
            "in": "path",
            "description": "The driver's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "If-Match",
            "in": "header",
            "description": "The `ETag` of your last read of the driver. Required: 428 `precondition.required` without it, 412 `concurrency.etag_mismatch` when the driver changed since.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateDriverRequest"
              },
              "example": {
                "phoneNumber": "+44 20 7946 0123"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "ETag": {
                "description": "The version of the resource. Send it back in `If-Match` when you update the resource.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Driver"
                },
                "example": {
                  "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945",
                  "firstName": "Amira",
                  "lastName": "Khan",
                  "name": "Amira Khan",
                  "phoneNumber": "+44 20 7946 0123",
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "currentVehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `driver.not_found`: The driver does not exist or is outside your client's access. Check the id. On a vehicle route it can also mean the driver does not drive that vehicle.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "driver.not_found": {
                    "$ref": "#/components/examples/driver.not_found"
                  }
                }
              }
            }
          },
          "412": {
            "description": "Precondition Failed. `errorCode`:\n\n- `concurrency.etag_mismatch`: The resource changed since you read it: `If-Match` no longer matches its `ETag`. Read it again, then retry the update.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "concurrency.etag_mismatch": {
                    "$ref": "#/components/examples/concurrency.etag_mismatch"
                  }
                }
              }
            }
          },
          "428": {
            "description": "Precondition Required. `errorCode`:\n\n- `precondition.required`: A required header is missing: `If-Match` when you update a vehicle or a driver, `Idempotency-Key` when you create a media request. Send it and retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "precondition.required": {
                    "$ref": "#/components/examples/precondition.required"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "drivers.write"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Drivers"
        ],
        "summary": "Delete a driver",
        "description": "Deletes the driver and unassigns them from their vehicle. Reading the driver is then 404 `driver.not_found`.\n\n**Required scope:** `drivers.write`",
        "operationId": "deleteDriver",
        "parameters": [
          {
            "name": "driverId",
            "in": "path",
            "description": "The driver's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `driver.not_found`: The driver does not exist or is outside your client's access. Check the id. On a vehicle route it can also mean the driver does not drive that vehicle.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "driver.not_found": {
                    "$ref": "#/components/examples/driver.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "drivers.write"
            ]
          }
        ]
      }
    },
    "/v1/events": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "List events",
        "description": "Lists the events devices raised (harsh driving, fatigue, panic button and more), newest trigger time first by default.\n\n- **Time range.** `from`/`to` bound the trigger time, `receivedFrom`/`receivedTo` the time Spillard received the event; both apply when both are sent. Without any, the last 24 hours of the order's time axis. A lone `from` or `receivedFrom` runs to now. Each range spans at most 92 days, otherwise 400 `validation.failed`.\n- **What is listed.** Requested events (created by video requests) are listed; unclassified events only with `classifications=unclassified`.\n- **Incremental sync.** List with `order=received_at_asc` and `receivedFrom` set to the `receivedAt` of the last event you processed. Events of that instant come again, so skip the ids you already have.\n- **Paging.** 100 events per page by default, at most 500 (`limit`). Follow `nextCursor`; a cursor belongs to the `order` it was issued for (400 `validation.cursor_invalid` otherwise). The list takes no `count` and has no `totalCount`.\n\nAn organization whose data access is limited to 30 days (`dataAccessDays` in `GET /v1/context`) gets no event triggered before then; a page may then hold fewer items than `limit` while `hasMore` is true.\n\n- `mediaStatus` is `available` (media arrived), `pending` (a media request for the event is still open) or `none`.\n- `include=shareUrl` adds `share` to each event: a public link to its page in the Spillard web app, valid for 30 days (until the event is 30 days old, for an organization limited to 30 days). It needs the `media.read` scope too (403 `auth.scope_missing` without it). The link cannot be revoked before it expires, so treat it as a secret. `share` is null for an event whose fleet, or its device's fleet, requires a video audit form, and for an event whose page would show data outside your client's access (its device has left that access, or some of its media lie outside it).\n\n**Required scope:** `events.read`",
        "operationId": "listEvents",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Only events triggered at or after this instant (UTC, ISO 8601). Both windows apply when both are sent; each may span at most 92 days (longer is 400 `validation.failed`). Without any window, the last 24 hours apply.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Only events triggered at or before this instant (UTC, ISO 8601); now when only `from` is given.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "receivedFrom",
            "in": "query",
            "description": "Only events Spillard received at or after this instant (UTC, ISO 8601). Both windows apply when both are sent; each may span at most 92 days (longer is 400 `validation.failed`). Without any window, the last 24 hours apply.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "receivedTo",
            "in": "query",
            "description": "Only events Spillard received at or before this instant (UTC, ISO 8601); now when only `receivedFrom` is given.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "fleetIds",
            "in": "query",
            "description": "Only the data of these fleets (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "hardwareIds",
            "in": "query",
            "description": "Only the data of the devices with these hardware ids (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "Only the data of this organization and, unless `includeSubOrganizations=false`, of the organizations below it (sub-organizations) that your client may read. It must be an organization your client may read (see `access` in `GET /v1/context`; an organization below one granted with its sub-organizations counts), otherwise 403 `auth.tenant_forbidden`. For an organization your client may read for some fleets only, only those fleets' data is returned.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "organizationIds",
            "in": "query",
            "description": "Only the data of these organizations. Ids outside the organizations your client may read match nothing (never a 403). For an organization your client may read for some fleets only, only those fleets' data is returned. Applied after `organizationId` and `includeSubOrganizations`: it narrows their result and never adds the organizations below an id.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "types",
            "in": "query",
            "description": "Only events of one of these types (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/EventType"
              }
            }
          },
          {
            "name": "classifications",
            "in": "query",
            "description": "Only events of one of these classifications. Without it every classification except `unclassified` is listed; `requested` events (video requests) are listed by default.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/EventClassification"
              }
            }
          },
          {
            "name": "includeSubOrganizations",
            "in": "query",
            "description": "Whether sub-organizations are included: the organizations below `organizationId` (or, without it, below the organizations your client was granted); default true. With `organizationId`: true returns that organization and the organizations below it that your client may read, false that organization only. Without `organizationId`: true returns everything your client may read, false only the organizations your client was granted, without the organizations below them. It never widens what your client may read. `organizationIds` applies afterwards: it narrows the result to the organizations it lists and never adds the organizations below an id.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "categories",
            "in": "query",
            "description": "Only events of one of these categories (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/EventCategory"
              }
            }
          },
          {
            "name": "vehicleIds",
            "in": "query",
            "description": "Only events of one of these vehicles (the vehicle the device was fitted to at trigger time).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "driverIds",
            "in": "query",
            "description": "Only events of one of these drivers (the driver assigned to the vehicle at trigger time).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "order",
            "in": "query",
            "description": "The order of the list: newest trigger time first by default, newest received time first when only a received window is given. Events of the same second come in a fixed order that is not always their order within that second. `received_at_asc` suits an incremental sync. A `cursor` belongs to the order it was issued for.",
            "schema": {
              "$ref": "#/components/schemas/EventOrder"
            }
          },
          {
            "name": "view",
            "in": "query",
            "description": "`basic` leaves out the per-sample arrays (`gSensorFrames`, `gyroscopeFrames`, `speedPointsKph`, `headingPointsDegrees`) for a cheap synchronization; `full` (the default) returns every field.",
            "schema": {
              "$ref": "#/components/schemas/EventView"
            }
          },
          {
            "name": "include",
            "in": "query",
            "description": "What to add to each event: `shareUrl` adds `share`, a public link to the event's page in the Spillard web app (working for 30 days; it shows the event and its media without a sign-in). `shareUrl` needs the `media.read` scope too: 403 `auth.scope_missing` without it.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/EventInclude"
              }
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/EventLimit"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventPage"
                },
                "example": {
                  "items": [
                    {
                      "id": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                      "triggeredAt": "2026-09-29T07:41:12.000Z",
                      "receivedAt": "2026-09-29T07:41:15.000Z",
                      "eventTypes": [
                        "driver_behaviour.harsh_braking"
                      ],
                      "categories": [
                        "driver_behaviour"
                      ],
                      "classification": "high",
                      "origin": "device",
                      "vendorEventType": "HARSH_BRAKE",
                      "firmwareVersion": "4.2.17",
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                        "registrationNumber": "SL24 WHK"
                      },
                      "driver": {
                        "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                      },
                      "device": {
                        "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                        "hardwareId": "009a2c41f7"
                      },
                      "locations": [
                        {
                          "latitude": 51.518392,
                          "longitude": -0.281046
                        }
                      ],
                      "address": {
                        "label": "Western Avenue (A40), Ealing, London, United Kingdom",
                        "houseNumber": null,
                        "street": "Western Avenue",
                        "district": "Ealing",
                        "city": "London",
                        "county": "Greater London",
                        "state": "England",
                        "postalCode": "W3 0TA",
                        "country": "United Kingdom"
                      },
                      "speedKph": 52,
                      "maxSpeedKph": 61,
                      "speedLimitKph": 64,
                      "speedPointsKph": [
                        61,
                        52,
                        31
                      ],
                      "headingPointsDegrees": [
                        92,
                        92,
                        91
                      ],
                      "gSensorFrames": [
                        {
                          "sequenceNumber": 0,
                          "sampledAt": "2026-09-29T07:41:12.000Z",
                          "xAxis": 0.02,
                          "yAxis": -0.61,
                          "zAxis": 1.01
                        }
                      ],
                      "gyroscopeFrames": null,
                      "hasMedia": true,
                      "mediaStatus": "available",
                      "share": null
                    }
                  ],
                  "hasMore": true,
                  "nextCursor": "eyJ0IjoiMjAyNjA5MjlUMDc0MTEyIn0",
                  "meta": {
                    "effectiveFrom": "2026-09-28T08:05:00.000Z",
                    "effectiveTo": "2026-09-29T08:05:00.000Z",
                    "effectiveReceivedFrom": null,
                    "effectiveReceivedTo": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "events.read"
            ]
          }
        ]
      }
    },
    "/v1/events/{eventId}": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "Get an event",
        "description": "Returns one event your client may read: the `links.self` of a `spillard.event.raised.v1` message. Any other id is 404 `event.not_found`.\n\nAn event older than its organization's data access (`dataAccessDays` in `GET /v1/context`) is 403 `data.retention_restricted`.\n\n- `mediaStatus` is `available` (media arrived), `pending` (a media request for the event is still open) or `none`.\n- `include=shareUrl` adds `share` to each event: a public link to its page in the Spillard web app, valid for 30 days (until the event is 30 days old, for an organization limited to 30 days). It needs the `media.read` scope too (403 `auth.scope_missing` without it). The link cannot be revoked before it expires, so treat it as a secret. `share` is null for an event whose fleet, or its device's fleet, requires a video audit form, and for an event whose page would show data outside your client's access (its device has left that access, or some of its media lie outside it).\n\n**Required scope:** `events.read`",
        "operationId": "getEvent",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "description": "The event's id, as `id` in an event response. An opaque id: treat it as text of up to 255 characters; do not parse it. An event id as base64 text of `{id},{hardwareId}` (also URL-safe or percent-encoded) is accepted too; responses never carry that form.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include",
            "in": "query",
            "description": "What to add to each event: `shareUrl` adds `share`, a public link to the event's page in the Spillard web app (working for 30 days; it shows the event and its media without a sign-in). `shareUrl` needs the `media.read` scope too: 403 `auth.scope_missing` without it.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/EventInclude"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceEvent"
                },
                "example": {
                  "id": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                  "triggeredAt": "2026-09-29T07:41:12.000Z",
                  "receivedAt": "2026-09-29T07:41:15.000Z",
                  "eventTypes": [
                    "driver_behaviour.harsh_braking"
                  ],
                  "categories": [
                    "driver_behaviour"
                  ],
                  "classification": "high",
                  "origin": "device",
                  "vendorEventType": "HARSH_BRAKE",
                  "firmwareVersion": "4.2.17",
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                    "registrationNumber": "SL24 WHK"
                  },
                  "driver": {
                    "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                  },
                  "device": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "locations": [
                    {
                      "latitude": 51.518392,
                      "longitude": -0.281046
                    }
                  ],
                  "address": {
                    "label": "Western Avenue (A40), Ealing, London, United Kingdom",
                    "houseNumber": null,
                    "street": "Western Avenue",
                    "district": "Ealing",
                    "city": "London",
                    "county": "Greater London",
                    "state": "England",
                    "postalCode": "W3 0TA",
                    "country": "United Kingdom"
                  },
                  "speedKph": 52,
                  "maxSpeedKph": 61,
                  "speedLimitKph": 64,
                  "speedPointsKph": [
                    61,
                    52,
                    31
                  ],
                  "headingPointsDegrees": [
                    92,
                    92,
                    91
                  ],
                  "gSensorFrames": [
                    {
                      "sequenceNumber": 0,
                      "sampledAt": "2026-09-29T07:41:12.000Z",
                      "xAxis": 0.02,
                      "yAxis": -0.61,
                      "zAxis": 1.01
                    }
                  ],
                  "gyroscopeFrames": null,
                  "hasMedia": true,
                  "mediaStatus": "available",
                  "share": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `data.retention_restricted`: The data is older than the organization's data access allows (see `dataAccessDays` in `GET /v1/context`). Ask for newer data.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "data.retention_restricted": {
                    "$ref": "#/components/examples/data.retention_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `event.not_found`: The event does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "event.not_found": {
                    "$ref": "#/components/examples/event.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "events.read"
            ]
          }
        ]
      }
    },
    "/v1/events/{eventId}:share": {
      "post": {
        "tags": [
          "Events"
        ],
        "summary": "Share an event through a public link",
        "description": "Creates a public link to the event's page in the Spillard web app: anyone holding it sees the event and its media without signing in until `expiresAt`.\n\n- **Expiry.** `expiresAt` must be in the future and at most 30 days ahead (30 days when not given), otherwise 400 `validation.failed`. For an organization limited to 30 days, the link ends when the event turns 30 days old, if that comes first.\n- **Treat it as a secret.** The link cannot be revoked before it expires, so keep `expiresAt` as short as you can. The share is listed with the organization's shared events in the Spillard web app.\n- **Scopes.** Needs `media.read` and `events.read` (403 `auth.scope_missing` without either): the page shows the event's details too.\n- **Refused.** An event your client may not read is 404 `event.not_found`. An event older than its organization's data access (`dataAccessDays` in `GET /v1/context`) is 403 `data.retention_restricted`. An event whose page would show data outside your client's access (its device has left the event's organization or your access, or some of its media lie outside it) is 403 `event.share_restricted`. An event whose fleet, or its device's fleet, requires a video audit form is 422 `audit_form.required`: share it in the Spillard web app.\n- `Idempotency-Key` is optional.\n\n**Required scope:** `media.read`",
        "operationId": "shareEvent",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "description": "The event's id, as `id` in an event response. An opaque id: treat it as text of up to 255 characters; do not parse it. An event id as base64 text of `{id},{hardwareId}` (also URL-safe or percent-encoded) is accepted too; responses never carry that form.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Optional. A unique value per logical request (for example a UUID). A retry with the same key and body within 24 hours returns the first result instead of acting twice; the same key with another body is 409 `idempotency.key_conflict`. While the first request is still running the answer is 409 `idempotency.in_progress`: wait, then repeat the same request with the same key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShareEventRequest"
              },
              "example": {
                "name": "Near miss on the A40",
                "description": "For the insurer",
                "expiresAt": "2026-10-06T12:00:00.000Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventShare"
                },
                "example": {
                  "id": "0c7d3e91-2a4b-4f68-b5e1-9d8c6a3f2b47",
                  "event": {
                    "id": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53"
                  },
                  "url": "https://www.spillard.live/shared-event/ZTVjN2E5MTItNGIzZC00Zjg2LTlhMmUtMGQxYzhiNmY3ZTUzLDAwOWEyYzQxZjcsMGM3ZDNlOTEtMmE0Yi00ZjY4LWI1ZTEtOWQ4YzZhM2YyYjQ3",
                  "expiresAt": "2026-10-06T12:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `data.retention_restricted`: The data is older than the organization's data access allows (see `dataAccessDays` in `GET /v1/context`). Ask for newer data.\n- `event.share_restricted`: The event cannot be shared by link: its page would show data outside what your client may read. Do not share this event, or ask your Spillard account manager for wider access for your client.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "data.retention_restricted": {
                    "$ref": "#/components/examples/data.retention_restricted"
                  },
                  "event.share_restricted": {
                    "$ref": "#/components/examples/event.share_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `event.not_found`: The event does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "event.not_found": {
                    "$ref": "#/components/examples/event.not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `idempotency.in_progress`: The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.\n- `idempotency.key_conflict`: The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "idempotency.in_progress": {
                    "$ref": "#/components/examples/idempotency.in_progress"
                  },
                  "idempotency.key_conflict": {
                    "$ref": "#/components/examples/idempotency.key_conflict"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Entity. `errorCode`:\n\n- `audit_form.required`: The fleet requires a video audit form for this action, which the API cannot submit. Do it in the Spillard web app.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "audit_form.required": {
                    "$ref": "#/components/examples/audit_form.required"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "media.read"
            ]
          }
        ]
      }
    },
    "/v1/fleets": {
      "get": {
        "tags": [
          "Fleets"
        ],
        "summary": "List fleets",
        "description": "Lists the fleets of the organizations your client may read, ordered by `id`. For an organization your client may read for some fleets only, only those fleets are listed. `name` matches part of the fleet's name (at most 100 characters); `organizationIds` only narrows the list.\n\n**Required scope:** `fleets.read`",
        "operationId": "listFleets",
        "parameters": [
          {
            "name": "organizationId",
            "in": "query",
            "description": "Only the data of this organization and, unless `includeSubOrganizations=false`, of the organizations below it (sub-organizations) that your client may read. It must be an organization your client may read (see `access` in `GET /v1/context`; an organization below one granted with its sub-organizations counts), otherwise 403 `auth.tenant_forbidden`. For an organization your client may read for some fleets only, only those fleets' data is returned.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "organizationIds",
            "in": "query",
            "description": "Only the data of these organizations. Ids outside the organizations your client may read match nothing (never a 403). For an organization your client may read for some fleets only, only those fleets' data is returned. Applied after `organizationId` and `includeSubOrganizations`: it narrows their result and never adds the organizations below an id.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "includeSubOrganizations",
            "in": "query",
            "description": "Whether sub-organizations are included: the organizations below `organizationId` (or, without it, below the organizations your client was granted); default true. With `organizationId`: true returns that organization and the organizations below it that your client may read, false that organization only. Without `organizationId`: true returns everything your client may read, false only the organizations your client was granted, without the organizations below them. It never widens what your client may read. `organizationIds` applies afterwards: it narrows the result to the organizations it lists and never adds the organizations below an id.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "name",
            "in": "query",
            "description": "Only fleets whose name contains this text (case-insensitive; at most 100 characters).",
            "schema": {
              "maxLength": 100,
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FleetPage"
                },
                "example": {
                  "items": [
                    {
                      "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35",
                      "name": "London deliveries",
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                        "name": "Spillard Logistics"
                      },
                      "deviceCount": 12,
                      "assignedDeviceCount": 11,
                      "unassignedDeviceCount": 1
                    },
                    {
                      "id": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80",
                      "name": "Manchester deliveries",
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                        "name": "Spillard Logistics"
                      },
                      "deviceCount": 8,
                      "assignedDeviceCount": 8,
                      "unassignedDeviceCount": 0
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "fleets.read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Fleets"
        ],
        "summary": "Create a fleet",
        "description": "Creates a fleet in `organizationId` and returns it with its `ETag`.\n\n- `name` is required, at most 100 characters (400 `validation.failed` otherwise).\n- The organization must be one your client reads with every fleet: one it reads for some fleets only is 403 `auth.fleet_restricted`, because the new fleet would not be among your client's fleets; one outside your access is 403 `auth.tenant_forbidden`.\n- Send an `Idempotency-Key` to retry safely: within 24 hours a retry with the same key and body returns the first result.\n\n**Required scope:** `fleets.write`",
        "operationId": "createFleet",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Optional. A unique value per logical request (for example a UUID). A retry with the same key and body within 24 hours returns the first result instead of acting twice; the same key with another body is 409 `idempotency.key_conflict`. While the first request is still running the answer is 409 `idempotency.in_progress`: wait, then repeat the same request with the same key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateFleetRequest"
              },
              "example": {
                "name": "London deliveries",
                "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "ETag": {
                "description": "The version of the resource. Send it back in `If-Match` when you update the resource.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Fleet"
                },
                "example": {
                  "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35",
                  "name": "London deliveries",
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                    "name": "Spillard Logistics"
                  },
                  "deviceCount": 12,
                  "assignedDeviceCount": 11,
                  "unassignedDeviceCount": 1
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `idempotency.in_progress`: The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.\n- `idempotency.key_conflict`: The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "idempotency.in_progress": {
                    "$ref": "#/components/examples/idempotency.in_progress"
                  },
                  "idempotency.key_conflict": {
                    "$ref": "#/components/examples/idempotency.key_conflict"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "fleets.write"
            ]
          }
        ]
      }
    },
    "/v1/fleets/{fleetId}": {
      "get": {
        "tags": [
          "Fleets"
        ],
        "summary": "Get a fleet",
        "description": "Returns one fleet with its organization and device counts, and its `ETag`. A fleet your client may not read is 404 `fleet.not_found`.\n\n**Required scope:** `fleets.read`",
        "operationId": "getFleet",
        "parameters": [
          {
            "name": "fleetId",
            "in": "path",
            "description": "The fleet's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "ETag": {
                "description": "The version of the resource. Send it back in `If-Match` when you update the resource.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Fleet"
                },
                "example": {
                  "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35",
                  "name": "London deliveries",
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                    "name": "Spillard Logistics"
                  },
                  "deviceCount": 12,
                  "assignedDeviceCount": 11,
                  "unassignedDeviceCount": 1
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `fleet.not_found`: The fleet does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "fleet.not_found": {
                    "$ref": "#/components/examples/fleet.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "fleets.read"
            ]
          }
        ]
      }
    },
    "/v1/journeys": {
      "get": {
        "tags": [
          "Journeys"
        ],
        "summary": "List journeys",
        "description": "Lists the journeys your client may read, ordered by end time, newest first.\n\n- `from`/`to` bound the start time. Without `from`, the 24 hours before `to` (now when `to` is not given). The range spans at most 90 days, otherwise 400 `validation.failed`.\n- 100 journeys per page by default, at most 1,000 (`limit`). Follow `nextCursor`.\n- An organization whose data access is limited to 30 days (`dataAccessDays` in `GET /v1/context`) gets no journey that started before then.\n\n**Required scope:** `journeys.read`",
        "operationId": "listJourneys",
        "parameters": [
          {
            "name": "organizationId",
            "in": "query",
            "description": "Only the data of this organization and, unless `includeSubOrganizations=false`, of the organizations below it (sub-organizations) that your client may read. It must be an organization your client may read (see `access` in `GET /v1/context`; an organization below one granted with its sub-organizations counts), otherwise 403 `auth.tenant_forbidden`. For an organization your client may read for some fleets only, only those fleets' data is returned.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "fleetIds",
            "in": "query",
            "description": "Only the data of these fleets (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "vehicleIds",
            "in": "query",
            "description": "Only journeys of these vehicles (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "organizationIds",
            "in": "query",
            "description": "Only the data of these organizations. Ids outside the organizations your client may read match nothing (never a 403). For an organization your client may read for some fleets only, only those fleets' data is returned. Applied after `organizationId` and `includeSubOrganizations`: it narrows their result and never adds the organizations below an id.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Only journeys that started at or after this instant (UTC, ISO 8601). Without `from`, the 24 hours before `to`. The window spans at most 90 days (400 `validation.failed` otherwise).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Only journeys that started at or before this instant (UTC, ISO 8601); now when not given.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "includeSubOrganizations",
            "in": "query",
            "description": "Whether sub-organizations are included: the organizations below `organizationId` (or, without it, below the organizations your client was granted); default true. With `organizationId`: true returns that organization and the organizations below it that your client may read, false that organization only. Without `organizationId`: true returns everything your client may read, false only the organizations your client was granted, without the organizations below them. It never widens what your client may read. `organizationIds` applies afterwards: it narrows the result to the organizations it lists and never adds the organizations below an id.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JourneyPage"
                },
                "example": {
                  "items": [
                    {
                      "id": "20260929T065804000Z009a2c41f7",
                      "startedAt": "2026-09-29T06:58:04.000Z",
                      "endedAt": "2026-09-29T07:52:31.000Z",
                      "durationSeconds": 3267,
                      "distanceMeters": 18420,
                      "idleSeconds": 214,
                      "maxSpeedKph": 72,
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                      },
                      "device": {
                        "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                        "hardwareId": "009a2c41f7"
                      },
                      "start": {
                        "address": {
                          "label": "Horsenden Lane South, Greenford, London, United Kingdom",
                          "street": "Horsenden Lane South",
                          "city": "London",
                          "postalCode": "UB6 7NS",
                          "country": "United Kingdom"
                        },
                        "location": {
                          "latitude": 51.540913,
                          "longitude": -0.346287
                        }
                      },
                      "end": {
                        "address": {
                          "label": "Bath Road, Hounslow, London, United Kingdom",
                          "street": "Bath Road",
                          "city": "London",
                          "postalCode": "TW6 2AA",
                          "country": "United Kingdom"
                        },
                        "location": {
                          "latitude": 51.481022,
                          "longitude": -0.429587
                        }
                      }
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "journeys.read"
            ]
          }
        ]
      }
    },
    "/v1/journeys/{journeyId}": {
      "get": {
        "tags": [
          "Journeys"
        ],
        "summary": "Get a journey",
        "description": "Returns one journey: its start and end, distance (meters), duration and idle time (seconds), maximum speed (kph), speeding count and GPS coverage. The positions and the events of the journey are separate lists. A journey your client may not read is 404 `journey.not_found`.\n\nA journey that started before its organization's data access (`dataAccessDays` in `GET /v1/context`) is 403 `data.retention_restricted`.\n\n**Required scope:** `journeys.read`",
        "operationId": "getJourney",
        "parameters": [
          {
            "name": "journeyId",
            "in": "path",
            "description": "The journey's id (a string, not a UUID), as `GET /v1/journeys` lists it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Journey"
                },
                "example": {
                  "id": "20260929T065804000Z009a2c41f7",
                  "startedAt": "2026-09-29T06:58:04.000Z",
                  "endedAt": "2026-09-29T07:52:31.000Z",
                  "durationSeconds": 3267,
                  "distanceMeters": 18420,
                  "idleSeconds": 214,
                  "maxSpeedKph": 72,
                  "speedingCount": 1,
                  "gpsCoverage": "full",
                  "frameCount": 327,
                  "revision": 1,
                  "processedAt": "2026-09-29T07:55:10.000Z",
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                  },
                  "device": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "start": {
                    "address": {
                      "label": "Horsenden Lane South, Greenford, London, United Kingdom",
                      "street": "Horsenden Lane South",
                      "city": "London",
                      "postalCode": "UB6 7NS",
                      "country": "United Kingdom"
                    },
                    "location": {
                      "latitude": 51.540913,
                      "longitude": -0.346287
                    }
                  },
                  "end": {
                    "address": {
                      "label": "Bath Road, Hounslow, London, United Kingdom",
                      "street": "Bath Road",
                      "city": "London",
                      "postalCode": "TW6 2AA",
                      "country": "United Kingdom"
                    },
                    "location": {
                      "latitude": 51.481022,
                      "longitude": -0.429587
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `data.retention_restricted`: The data is older than the organization's data access allows (see `dataAccessDays` in `GET /v1/context`). Ask for newer data.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "data.retention_restricted": {
                    "$ref": "#/components/examples/data.retention_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `journey.not_found`: The journey does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "journey.not_found": {
                    "$ref": "#/components/examples/journey.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "journeys.read"
            ]
          }
        ]
      }
    },
    "/v1/journeys/{journeyId}/frames": {
      "get": {
        "tags": [
          "Journeys"
        ],
        "summary": "List a journey's positions",
        "description": "Lists the positions (frames) recorded during the journey (time, location, speed in kph, heading in degrees), oldest first.\n\n- 1,000 positions per page by default, at most 10,000 (`limit`). Follow `nextCursor`.\n- Send `Accept-Encoding: br, gzip` to receive the pages compressed.\n\nA journey that started before its organization's data access (`dataAccessDays` in `GET /v1/context`) is 403 `data.retention_restricted`.\n\n**Required scope:** `journeys.read`",
        "operationId": "listJourneyFrames",
        "parameters": [
          {
            "name": "journeyId",
            "in": "path",
            "description": "The journey's id (a string, not a UUID), as `GET /v1/journeys` lists it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/FrameLimit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TelemetryFramePage"
                },
                "example": {
                  "items": [
                    {
                      "location": {
                        "latitude": 51.540913,
                        "longitude": -0.346287
                      },
                      "recordedAt": "2026-09-29T06:58:04.000Z",
                      "speedKph": 0,
                      "headingDegrees": 184
                    },
                    {
                      "location": {
                        "latitude": 51.539871,
                        "longitude": -0.346402
                      },
                      "recordedAt": "2026-09-29T06:58:14.000Z",
                      "speedKph": 23,
                      "headingDegrees": 187
                    },
                    {
                      "location": {
                        "latitude": 51.538127,
                        "longitude": -0.346951
                      },
                      "recordedAt": "2026-09-29T06:58:24.000Z",
                      "speedKph": 38,
                      "headingDegrees": 190
                    }
                  ],
                  "hasMore": true,
                  "nextCursor": "eyJvIjozfQ"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `data.retention_restricted`: The data is older than the organization's data access allows (see `dataAccessDays` in `GET /v1/context`). Ask for newer data.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "data.retention_restricted": {
                    "$ref": "#/components/examples/data.retention_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `journey.not_found`: The journey does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "journey.not_found": {
                    "$ref": "#/components/examples/journey.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "journeys.read"
            ]
          }
        ]
      }
    },
    "/v1/journeys/{journeyId}/events": {
      "get": {
        "tags": [
          "Journeys"
        ],
        "summary": "List a journey's events",
        "description": "Lists the events the journey's device raised between the journey's start and end, newest first. Requested events are listed; unclassified ones are not. 100 events per page by default, at most 500 (`limit`); follow `nextCursor`.\n\nA journey that started before its organization's data access (`dataAccessDays` in `GET /v1/context`) is 403 `data.retention_restricted`.\n\n- `mediaStatus` is `available` (media arrived), `pending` (a media request for the event is still open) or `none`.\n- `include=shareUrl` adds `share` to each event: a public link to its page in the Spillard web app, valid for 30 days (until the event is 30 days old, for an organization limited to 30 days). It needs the `media.read` scope too (403 `auth.scope_missing` without it). The link cannot be revoked before it expires, so treat it as a secret. `share` is null for an event whose fleet, or its device's fleet, requires a video audit form, and for an event whose page would show data outside your client's access (its device has left that access, or some of its media lie outside it).\n\n**Required scope:** `events.read`",
        "operationId": "listJourneyEvents",
        "parameters": [
          {
            "name": "journeyId",
            "in": "path",
            "description": "The journey's id (a string, not a UUID), as `GET /v1/journeys` lists it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include",
            "in": "query",
            "description": "What to add to each event: `shareUrl` adds `share`, a public link to the event's page in the Spillard web app (working for 30 days; it shows the event and its media without a sign-in). `shareUrl` needs the `media.read` scope too: 403 `auth.scope_missing` without it.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/EventInclude"
              }
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/EventLimit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeviceEventPage"
                },
                "example": {
                  "items": [
                    {
                      "id": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                      "triggeredAt": "2026-09-29T07:41:12.000Z",
                      "receivedAt": "2026-09-29T07:41:15.000Z",
                      "eventTypes": [
                        "driver_behaviour.harsh_braking"
                      ],
                      "categories": [
                        "driver_behaviour"
                      ],
                      "classification": "high",
                      "origin": "device",
                      "vendorEventType": "HARSH_BRAKE",
                      "firmwareVersion": "4.2.17",
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                        "registrationNumber": "SL24 WHK"
                      },
                      "driver": {
                        "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                      },
                      "device": {
                        "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                        "hardwareId": "009a2c41f7"
                      },
                      "locations": [
                        {
                          "latitude": 51.518392,
                          "longitude": -0.281046
                        }
                      ],
                      "address": {
                        "label": "Western Avenue (A40), Ealing, London, United Kingdom",
                        "houseNumber": null,
                        "street": "Western Avenue",
                        "district": "Ealing",
                        "city": "London",
                        "county": "Greater London",
                        "state": "England",
                        "postalCode": "W3 0TA",
                        "country": "United Kingdom"
                      },
                      "speedKph": 52,
                      "maxSpeedKph": 61,
                      "speedLimitKph": 64,
                      "speedPointsKph": [
                        61,
                        52,
                        31
                      ],
                      "headingPointsDegrees": [
                        92,
                        92,
                        91
                      ],
                      "gSensorFrames": [
                        {
                          "sequenceNumber": 0,
                          "sampledAt": "2026-09-29T07:41:12.000Z",
                          "xAxis": 0.02,
                          "yAxis": -0.61,
                          "zAxis": 1.01
                        }
                      ],
                      "gyroscopeFrames": null,
                      "hasMedia": true,
                      "mediaStatus": "available",
                      "share": null
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null,
                  "meta": {
                    "effectiveFrom": "2026-09-29T06:58:04.000Z",
                    "effectiveTo": "2026-09-29T07:52:31.000Z",
                    "effectiveReceivedFrom": null,
                    "effectiveReceivedTo": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `data.retention_restricted`: The data is older than the organization's data access allows (see `dataAccessDays` in `GET /v1/context`). Ask for newer data.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "data.retention_restricted": {
                    "$ref": "#/components/examples/data.retention_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `journey.not_found`: The journey does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "journey.not_found": {
                    "$ref": "#/components/examples/journey.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "events.read"
            ]
          }
        ]
      }
    },
    "/v1/devices/{deviceId}/events/{eventId}/media": {
      "get": {
        "tags": [
          "Media"
        ],
        "summary": "List files of a device's event",
        "description": "Returns download links for the media of one event of the device.\n\n- The files are found by the device and the event id, so they stay listable after the event record has expired (`GET /v1/events/{eventId}/media` then answers 404).\n- A device that does not exist or that your client may not read is 404 `device.not_found`. An event id that names no file of the device (an unknown id, or an event your client may not read) gives an empty list.\n- Each link works until its `expiresAt`: 60 minutes by default, or the `linkTtlSeconds` you send (60 to 86,400 seconds; another value is 400 `validation.failed`). Read again for fresh links; do not store them.\n- `purpose` is `video` or `thumbnail` (the preview image of a clip); null for any other file.\n- The response is not cached or compressed (`Cache-Control: no-store`).\n- An organization whose data access is limited to 30 days (`dataAccessDays` in `GET /v1/context`) gets no files of footage older than that.\n\n**Required scope:** `media.read`",
        "operationId": "listDeviceEventMedia",
        "parameters": [
          {
            "name": "deviceId",
            "in": "path",
            "description": "The device's id (a UUID) or its hardware id. A UUID is looked up as a device id first; a value that is not the id of one of your devices is looked up as a hardware id (case-insensitive).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "eventId",
            "in": "path",
            "description": "The event's id, as `id` in an event response. An opaque id: treat it as text of up to 255 characters; do not parse it. An event id as base64 text of `{id},{hardwareId}` (also URL-safe or percent-encoded) is accepted too; responses never carry that form.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "linkTtlSeconds",
            "in": "query",
            "description": "How long the download links work, in seconds: 60 to 86,400 (24 hours); 3,600 (60 minutes) when not given. Each item's `expiresAt` says when its link stops working.",
            "schema": {
              "maximum": 86400,
              "minimum": 60,
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaList"
                },
                "example": {
                  "items": [
                    {
                      "id": "b3f7d2a8-4c19-4e65-9a0b-8d1e6c3f5a72",
                      "uri": "https://example.blob.core.windows.net/media/b3f7d2a8.mp4?se=2026-09-29T09%3A10%3A00Z&sp=r&sig=REDACTED",
                      "expiresAt": "2026-09-29T09:10:00.000Z",
                      "receivedAt": "2026-09-29T07:42:30.000Z",
                      "mimeType": "video/mp4",
                      "purpose": "video",
                      "fileName": "ch1_20260929_074105.mp4",
                      "sequenceNumber": 1,
                      "channel": {
                        "number": 1,
                        "name": "Road facing"
                      },
                      "command": null,
                      "event": {
                        "id": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53"
                      }
                    },
                    {
                      "id": "d8a4f1c6-2b93-4e07-b5d1-7c0e9a3f6b24",
                      "uri": "https://example.blob.core.windows.net/media/d8a4f1c6.jpg?se=2026-09-29T09%3A10%3A00Z&sp=r&sig=REDACTED",
                      "expiresAt": "2026-09-29T09:10:00.000Z",
                      "receivedAt": "2026-09-29T07:42:31.000Z",
                      "mimeType": "image/jpeg",
                      "purpose": "thumbnail",
                      "fileName": "ch1_20260929_074112.jpg",
                      "sequenceNumber": 2,
                      "channel": {
                        "number": 1,
                        "name": "Road facing"
                      },
                      "command": null,
                      "event": {
                        "id": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device.not_found`: The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device.not_found": {
                    "$ref": "#/components/examples/device.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "media.read"
            ]
          }
        ]
      }
    },
    "/v1/events/{eventId}/media": {
      "get": {
        "tags": [
          "Media"
        ],
        "summary": "List an event's files",
        "description": "Returns download links for the media of an event your client may read: the `links.media` of its `spillard.event.raised.v1` message. The list is empty while no file has arrived; an event your client may not read is 404 `event.not_found`.\n\n- Each link works until its `expiresAt`: 60 minutes by default, or the `linkTtlSeconds` you send (60 to 86,400 seconds; another value is 400 `validation.failed`). Read again for fresh links; do not store them.\n- `purpose` is `video` or `thumbnail` (the preview image of a clip); null for any other file.\n- The response is not cached or compressed (`Cache-Control: no-store`).\n- An organization whose data access is limited to 30 days (`dataAccessDays` in `GET /v1/context`) gets no files of footage older than that.\n\n**Required scope:** `media.read`",
        "operationId": "listEventMedia",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "description": "The event's id, as `id` in an event response. An opaque id: treat it as text of up to 255 characters; do not parse it. An event id as base64 text of `{id},{hardwareId}` (also URL-safe or percent-encoded) is accepted too; responses never carry that form.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "linkTtlSeconds",
            "in": "query",
            "description": "How long the download links work, in seconds: 60 to 86,400 (24 hours); 3,600 (60 minutes) when not given. Each item's `expiresAt` says when its link stops working.",
            "schema": {
              "maximum": 86400,
              "minimum": 60,
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaList"
                },
                "example": {
                  "items": [
                    {
                      "id": "b3f7d2a8-4c19-4e65-9a0b-8d1e6c3f5a72",
                      "uri": "https://example.blob.core.windows.net/media/b3f7d2a8.mp4?se=2026-09-29T09%3A10%3A00Z&sp=r&sig=REDACTED",
                      "expiresAt": "2026-09-29T09:10:00.000Z",
                      "receivedAt": "2026-09-29T07:42:30.000Z",
                      "mimeType": "video/mp4",
                      "purpose": "video",
                      "fileName": "ch1_20260929_074105.mp4",
                      "sequenceNumber": 1,
                      "channel": {
                        "number": 1,
                        "name": "Road facing"
                      },
                      "command": null,
                      "event": {
                        "id": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53"
                      }
                    },
                    {
                      "id": "d8a4f1c6-2b93-4e07-b5d1-7c0e9a3f6b24",
                      "uri": "https://example.blob.core.windows.net/media/d8a4f1c6.jpg?se=2026-09-29T09%3A10%3A00Z&sp=r&sig=REDACTED",
                      "expiresAt": "2026-09-29T09:10:00.000Z",
                      "receivedAt": "2026-09-29T07:42:31.000Z",
                      "mimeType": "image/jpeg",
                      "purpose": "thumbnail",
                      "fileName": "ch1_20260929_074112.jpg",
                      "sequenceNumber": 2,
                      "channel": {
                        "number": 1,
                        "name": "Road facing"
                      },
                      "command": null,
                      "event": {
                        "id": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `event.not_found`: The event does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "event.not_found": {
                    "$ref": "#/components/examples/event.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "media.read"
            ]
          }
        ]
      }
    },
    "/v1/events/{eventId}/media-requests": {
      "post": {
        "tags": [
          "Media"
        ],
        "summary": "Request video of an event",
        "description": "Requests a video clip (a video request) from the device that raised the event, around the time of the event. Returns 202 with the request's `id`. 202 means the request is queued; the device has not recorded anything yet.\n\n- **Time.** `startAt` is the start of the clip (UTC, ISO 8601). The clip lasts `durationSeconds` and ends at `startAt` plus `durationSeconds`. Without `startAt`, the clip is centered on the event's `triggeredAt`: it starts `durationSeconds` / 2 earlier. To start the clip at the event time, send `startAt` equal to `triggeredAt`.\n- **Duration.** `durationSeconds` is required: 1 to 120 seconds, otherwise 400 `validation.failed`.\n- **Event.** An event your client may not read is 404 `event.not_found`. `{eventId}` also takes the base64 form of an event id (`{id},{hardwareId}`).\n- **Idempotency.** `Idempotency-Key` is required (428 `precondition.required` without it). The key is reserved for 24 hours per API client. A retry with the same key and body returns the first result; the same key with another body is 409 `idempotency.key_conflict`.\n- **After a server error.** After a 500, 503 or 504 the request may or may not be queued. Repeat the same request with the same key. 409 `idempotency.in_progress` means the first request has not finished or ended without a known result: wait a few seconds, then repeat it again. If the answer stays 409, look for the request in `GET /v1/commands` (filters `hardwareIds`, `types` and `createdFrom`); if it is not listed, send it again with a new key.\n- **Then.** The request runs on the device and ends later. Poll `GET /v1/media-requests/{mediaRequestId}` until `state` is `completed`, `failed`, `cancelled` or `timed_out`. You can also wait for the `spillard.media.request.completed.v1` webhook, but do not rely on it alone. Only `completed` is reliable today; `failed` and `no_data` are in future development, do not rely on them. A request that times out may send no message.\n- **Files.** When `mediaReady` is true, get the download links from `GET /v1/media-requests/{mediaRequestId}/media`. The list is empty until then.\n- An organization whose data access is limited to 30 days (`dataAccessDays` in `GET /v1/context`) gets 403 `data.retention_restricted` for footage older than that.\n- A fleet that requires a video audit form gets 422 `audit_form.required`: make the request in the Spillard web app.\n- The request normally also appears in the Spillard web app's media requests and counts in the organization's media of the day. There is no daily media limit; 429 is the API rate limit.\n\n**Required scope:** `media.write`",
        "operationId": "createEventMediaRequest",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "description": "The event's id, as `id` in an event response. An opaque id: treat it as text of up to 255 characters; do not parse it. An event id as base64 text of `{id},{hardwareId}` (also URL-safe or percent-encoded) is accepted too; responses never carry that form.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique value per logical request (for example a UUID). Required: 428 `precondition.required` without it. A retry with the same key and body returns the first result; the same key with another body is 409 `idempotency.key_conflict`. Keys are kept for 24 hours per API client.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateEventMediaRequest"
              },
              "example": {
                "type": "video",
                "channels": [
                  1
                ],
                "durationSeconds": 20,
                "videoQuality": "sd",
                "externalId": "claim-2026-0194"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaRequestAccepted"
                },
                "example": {
                  "id": "9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683",
                  "device": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "event": {
                    "id": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53"
                  },
                  "links": {
                    "self": "/v1/media-requests/9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `data.retention_restricted`: The data is older than the organization's data access allows (see `dataAccessDays` in `GET /v1/context`). Ask for newer data.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "data.retention_restricted": {
                    "$ref": "#/components/examples/data.retention_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `event.not_found`: The event does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "event.not_found": {
                    "$ref": "#/components/examples/event.not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `idempotency.in_progress`: The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.\n- `idempotency.key_conflict`: The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "idempotency.in_progress": {
                    "$ref": "#/components/examples/idempotency.in_progress"
                  },
                  "idempotency.key_conflict": {
                    "$ref": "#/components/examples/idempotency.key_conflict"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Entity. `errorCode`:\n\n- `audit_form.required`: The fleet requires a video audit form for this action, which the API cannot submit. Do it in the Spillard web app.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "audit_form.required": {
                    "$ref": "#/components/examples/audit_form.required"
                  }
                }
              }
            }
          },
          "428": {
            "description": "Precondition Required. `errorCode`:\n\n- `precondition.required`: A required header is missing: `If-Match` when you update a vehicle or a driver, `Idempotency-Key` when you create a media request. Send it and retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "precondition.required": {
                    "$ref": "#/components/examples/precondition.required"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "media.write"
            ]
          }
        ]
      }
    },
    "/v1/commands/{commandId}/media": {
      "get": {
        "tags": [
          "Media"
        ],
        "summary": "List a command's files",
        "description": "Returns download links for the files a command produced (for a media request, the same as `GET /v1/media-requests/{mediaRequestId}/media`). A command your client may not read is 404 `command.not_found`.\n\n- Each link works until its `expiresAt`: 60 minutes by default, or the `linkTtlSeconds` you send (60 to 86,400 seconds; another value is 400 `validation.failed`). Read again for fresh links; do not store them.\n- `purpose` is `video` or `thumbnail` (the preview image of a clip); null for any other file.\n- The response is not cached or compressed (`Cache-Control: no-store`).\n- An organization whose data access is limited to 30 days (`dataAccessDays` in `GET /v1/context`) gets no files of footage older than that.\n\n**Required scope:** `media.read`",
        "operationId": "listCommandMedia",
        "parameters": [
          {
            "name": "commandId",
            "in": "path",
            "description": "The command's id, as `id` in a command response. An opaque id: treat it as text of up to 255 characters; do not parse it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "linkTtlSeconds",
            "in": "query",
            "description": "How long the download links work, in seconds: 60 to 86,400 (24 hours); 3,600 (60 minutes) when not given. Each item's `expiresAt` says when its link stops working.",
            "schema": {
              "maximum": 86400,
              "minimum": 60,
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaList"
                },
                "example": {
                  "items": [
                    {
                      "id": "6a1d9c3e-0b7f-4e52-8d4a-2c9f1e7b5a36",
                      "uri": "https://example.blob.core.windows.net/media/6a1d9c3e.mp4?se=2026-09-29T09%3A10%3A00Z&sp=r&sig=REDACTED",
                      "expiresAt": "2026-09-29T09:10:00.000Z",
                      "receivedAt": "2026-09-29T08:07:48.000Z",
                      "mimeType": "video/mp4",
                      "purpose": "video",
                      "fileName": "ch1_20260929_074000.mp4",
                      "sequenceNumber": 1,
                      "channel": {
                        "number": 1,
                        "name": "Road facing"
                      },
                      "command": {
                        "id": "9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683"
                      },
                      "event": {
                        "id": "1f3b8d27-6c4e-4a95-b0d2-7e9a5c1f3b68"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `command.not_found`: The command or media request does not exist or is outside your client's access. Check the id. The media request routes answer with this code too.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "command.not_found": {
                    "$ref": "#/components/examples/command.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "media.read"
            ]
          }
        ]
      }
    },
    "/v1/media-requests": {
      "post": {
        "tags": [
          "Media"
        ],
        "summary": "Request a video clip",
        "description": "Requests a video clip (a video request) from a device. Returns 202 with the request's `id`. 202 means the request is queued; the device has not recorded anything yet.\n\n- **What to send.** The device (exactly one of `hardwareId` and `deviceId`), `type` `video`, `channels`, the time (exactly one of `startAt` and `midpointAt`) and `durationSeconds`. Otherwise 400 `validation.failed`. Take the `channels` from the device (`GET /v1/devices/{deviceId}`): the API checks only that there are at most 32 and each is from 1 to 32.\n- **Time (UTC, ISO 8601).** `startAt` is the start of the clip. The clip lasts `durationSeconds` and ends at `startAt` plus `durationSeconds`. `midpointAt` is the middle of the clip: the clip starts `durationSeconds` / 2 earlier.\n- **Device.** A device your client may not read, or a device that is not assigned to an organization, is 404 `device.not_found`.\n- **Duration.** `durationSeconds` is required: 1 to 120 seconds, otherwise 400 `validation.failed`.\n- Spillard records the request as a `requested_data.video` event at the start of the clip (`event` in the response).\n- **Idempotency.** `Idempotency-Key` is required (428 `precondition.required` without it). The key is reserved for 24 hours per API client. A retry with the same key and body returns the first result; the same key with another body is 409 `idempotency.key_conflict`.\n- **After a server error.** After a 500, 503 or 504 the request may or may not be queued. Repeat the same request with the same key. 409 `idempotency.in_progress` means the first request has not finished or ended without a known result: wait a few seconds, then repeat it again. If the answer stays 409, look for the request in `GET /v1/commands` (filters `hardwareIds`, `types` and `createdFrom`); if it is not listed, send it again with a new key.\n- **Then.** The request runs on the device and ends later. Poll `GET /v1/media-requests/{mediaRequestId}` until `state` is `completed`, `failed`, `cancelled` or `timed_out`. You can also wait for the `spillard.media.request.completed.v1` webhook, but do not rely on it alone. Only `completed` is reliable today; `failed` and `no_data` are in future development, do not rely on them. A request that times out may send no message.\n- **Files.** When `mediaReady` is true, get the download links from `GET /v1/media-requests/{mediaRequestId}/media`. The list is empty until then.\n- An organization whose data access is limited to 30 days (`dataAccessDays` in `GET /v1/context`) gets 403 `data.retention_restricted` for footage older than that.\n- A fleet that requires a video audit form gets 422 `audit_form.required`: make the request in the Spillard web app.\n- The request normally also appears in the Spillard web app's media requests and counts in the organization's media of the day. There is no daily media limit; 429 is the API rate limit.\n\n**Required scope:** `media.write`",
        "operationId": "createMediaRequest",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique value per logical request (for example a UUID). Required: 428 `precondition.required` without it. A retry with the same key and body returns the first result; the same key with another body is 409 `idempotency.key_conflict`. Keys are kept for 24 hours per API client.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateMediaRequest"
              },
              "example": {
                "hardwareId": "009a2c41f7",
                "type": "video",
                "channels": [
                  1,
                  2
                ],
                "startAt": "2026-09-29T07:40:00.000Z",
                "durationSeconds": 30,
                "videoQuality": "hd",
                "overlay": true,
                "externalId": "claim-2026-0193"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaRequestAccepted"
                },
                "example": {
                  "id": "9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683",
                  "device": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "event": {
                    "id": "1f3b8d27-6c4e-4a95-b0d2-7e9a5c1f3b68"
                  },
                  "links": {
                    "self": "/v1/media-requests/9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `data.retention_restricted`: The data is older than the organization's data access allows (see `dataAccessDays` in `GET /v1/context`). Ask for newer data.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "data.retention_restricted": {
                    "$ref": "#/components/examples/data.retention_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device.not_found`: The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device.not_found": {
                    "$ref": "#/components/examples/device.not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `idempotency.in_progress`: The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.\n- `idempotency.key_conflict`: The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "idempotency.in_progress": {
                    "$ref": "#/components/examples/idempotency.in_progress"
                  },
                  "idempotency.key_conflict": {
                    "$ref": "#/components/examples/idempotency.key_conflict"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Entity. `errorCode`:\n\n- `audit_form.required`: The fleet requires a video audit form for this action, which the API cannot submit. Do it in the Spillard web app.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "audit_form.required": {
                    "$ref": "#/components/examples/audit_form.required"
                  }
                }
              }
            }
          },
          "428": {
            "description": "Precondition Required. `errorCode`:\n\n- `precondition.required`: A required header is missing: `If-Match` when you update a vehicle or a driver, `Idempotency-Key` when you create a media request. Send it and retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "precondition.required": {
                    "$ref": "#/components/examples/precondition.required"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "media.write"
            ]
          }
        ]
      }
    },
    "/v1/media-requests/{mediaRequestId}": {
      "get": {
        "tags": [
          "Media"
        ],
        "summary": "Get a media request",
        "description": "Returns the state of a video request: `state` and whether its files can be read (`mediaReady`).\n\n- While `state` is `queued`, `dispatched` or `acknowledged` the request is still running. It ends as `completed`, `failed`, `cancelled` or `timed_out`; `failureReason` then holds the text the device gave, if any.\n- Poll this route until `state` is final. You can also wait for the `spillard.media.request.completed.v1` webhook, but do not rely on it alone. Only `completed` is reliable today; `failed` and `no_data` are in future development, do not rely on them. A request that times out may send no message.\n- When `mediaReady` is true, get the files from `GET /v1/media-requests/{mediaRequestId}/media`.\n- A request your client may not read is 404 `command.not_found`.\n\n**Required scope:** `media.read`",
        "operationId": "getMediaRequest",
        "parameters": [
          {
            "name": "mediaRequestId",
            "in": "path",
            "description": "The media request's id: the `id` of the answer to a video request, equal to `mediaRequestId` in `spillard.media.request.completed.v1`. An opaque id: treat it as text of up to 255 characters; do not parse it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaRequest"
                },
                "example": {
                  "id": "9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683",
                  "state": "completed",
                  "mediaReady": true,
                  "externalId": "claim-2026-0193",
                  "failureReason": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `command.not_found`: The command or media request does not exist or is outside your client's access. Check the id. The media request routes answer with this code too.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "command.not_found": {
                    "$ref": "#/components/examples/command.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "media.read"
            ]
          }
        ]
      }
    },
    "/v1/media-requests/{mediaRequestId}/media": {
      "get": {
        "tags": [
          "Media"
        ],
        "summary": "List a media request's files",
        "description": "Returns download links for the files a video request produced (the clip and its preview image): the `links.media` of its `spillard.media.request.completed.v1` message, for every outcome. The list is empty until the device has uploaded something. A request your client may not read is 404 `command.not_found`.\n\n- Each link works until its `expiresAt`: 60 minutes by default, or the `linkTtlSeconds` you send (60 to 86,400 seconds; another value is 400 `validation.failed`). Read again for fresh links; do not store them.\n- `purpose` is `video` or `thumbnail` (the preview image of a clip); null for any other file.\n- The response is not cached or compressed (`Cache-Control: no-store`).\n- An organization whose data access is limited to 30 days (`dataAccessDays` in `GET /v1/context`) gets no files of footage older than that.\n\n**Required scope:** `media.read`",
        "operationId": "listMediaRequestMedia",
        "parameters": [
          {
            "name": "mediaRequestId",
            "in": "path",
            "description": "The media request's id: the `id` of the answer to a video request, equal to `mediaRequestId` in `spillard.media.request.completed.v1`. An opaque id: treat it as text of up to 255 characters; do not parse it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "linkTtlSeconds",
            "in": "query",
            "description": "How long the download links work, in seconds: 60 to 86,400 (24 hours); 3,600 (60 minutes) when not given. Each item's `expiresAt` says when its link stops working.",
            "schema": {
              "maximum": 86400,
              "minimum": 60,
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaList"
                },
                "example": {
                  "items": [
                    {
                      "id": "6a1d9c3e-0b7f-4e52-8d4a-2c9f1e7b5a36",
                      "uri": "https://example.blob.core.windows.net/media/6a1d9c3e.mp4?se=2026-09-29T09%3A10%3A00Z&sp=r&sig=REDACTED",
                      "expiresAt": "2026-09-29T09:10:00.000Z",
                      "receivedAt": "2026-09-29T08:07:48.000Z",
                      "mimeType": "video/mp4",
                      "purpose": "video",
                      "fileName": "ch1_20260929_074000.mp4",
                      "sequenceNumber": 1,
                      "channel": {
                        "number": 1,
                        "name": "Road facing"
                      },
                      "command": {
                        "id": "9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683"
                      },
                      "event": {
                        "id": "1f3b8d27-6c4e-4a95-b0d2-7e9a5c1f3b68"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `command.not_found`: The command or media request does not exist or is outside your client's access. Check the id. The media request routes answer with this code too.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "command.not_found": {
                    "$ref": "#/components/examples/command.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "media.read"
            ]
          }
        ]
      }
    },
    "/v1/metrics/human-detection-operational-time": {
      "get": {
        "tags": [
          "Metrics"
        ],
        "summary": "List human detection figures per day",
        "description": "Per vehicle and day (UTC): how long the vehicle's journeys ran (seconds) and how many classified human detection events it raised, by severity. A vehicle appears on a day with at least one such event.\n\n- `from` and `to` are required days (YYYY-MM-DD) of a half-open range: `from=2026-09-01&to=2026-09-03` covers 1 and 2 September. `to` must be after `from` and at most 31 days later, otherwise 400 `validation.failed`.\n- Rows are ordered by day, then organization, fleet and vehicle. 100 rows per page by default, at most 1,000 (`limit`); follow `nextCursor`.\n- An organization whose data access is limited to 30 days (`dataAccessDays` in `GET /v1/context`) gets no row for a day older than that.\n\n**Required scope:** `events.read`",
        "operationId": "listHumanDetectionOperationalTime",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "The first day of the range (UTC, YYYY-MM-DD). Required.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "The day after the last day of the range (UTC, YYYY-MM-DD; half-open, at most 31 days after `from`). Required.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "Only the data of this organization and, unless `includeSubOrganizations=false`, of the organizations below it (sub-organizations) that your client may read. It must be an organization your client may read (see `access` in `GET /v1/context`; an organization below one granted with its sub-organizations counts), otherwise 403 `auth.tenant_forbidden`. For an organization your client may read for some fleets only, only those fleets' data is returned.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "organizationIds",
            "in": "query",
            "description": "Only the data of these organizations. Ids outside the organizations your client may read match nothing (never a 403). For an organization your client may read for some fleets only, only those fleets' data is returned. Applied after `organizationId` and `includeSubOrganizations`: it narrows their result and never adds the organizations below an id.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "includeSubOrganizations",
            "in": "query",
            "description": "Whether sub-organizations are included: the organizations below `organizationId` (or, without it, below the organizations your client was granted); default true. With `organizationId`: true returns that organization and the organizations below it that your client may read, false that organization only. Without `organizationId`: true returns everything your client may read, false only the organizations your client was granted, without the organizations below them. It never widens what your client may read. `organizationIds` applies afterwards: it narrows the result to the organizations it lists and never adds the organizations below an id.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "fleetIds",
            "in": "query",
            "description": "Only vehicles of these fleets.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "vehicleIds",
            "in": "query",
            "description": "Only these vehicles.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HumanDetectionOperationalTimePage"
                },
                "example": {
                  "items": [
                    {
                      "date": "2026-09-28",
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                      },
                      "operationalSeconds": 19840,
                      "highCount": 2,
                      "mediumCount": 5,
                      "lowCount": 9
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "events.read"
            ]
          }
        ]
      }
    },
    "/v1/organizations": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "List organizations",
        "description": "Lists the organizations your client may read, ordered by `id`: those it was granted and, where a grant includes them, their sub-organizations.\n\n- `organizationIds` narrows the list; an id your client may not read matches nothing (never a 403).\n- `name` matches part of the organization's name.\n\n**Required scope:** `organizations.read`",
        "operationId": "listOrganizations",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "description": "Only organizations whose name contains this text.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "organizationIds",
            "in": "query",
            "description": "Only the data of these organizations. Ids outside the organizations your client may read match nothing (never a 403). For an organization your client may read for some fleets only, only those fleets' data is returned.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationPage"
                },
                "example": {
                  "items": [
                    {
                      "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                      "name": "Spillard Logistics",
                      "supportEmail": "support@example.com",
                      "supportPhone": "+44 20 7946 0000",
                      "timeZone": "Europe/London"
                    },
                    {
                      "id": "6e0b4c93-1a7d-4f52-8b3e-d9a2c5f71e04",
                      "name": "Spillard Logistics North",
                      "supportEmail": null,
                      "supportPhone": null,
                      "timeZone": "Europe/London"
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "organizations.read"
            ]
          }
        ]
      }
    },
    "/v1/organizations/{organizationId}": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "Get an organization",
        "description": "Returns one organization your client may read. Any other id is 404 `organization.not_found`, whether the organization exists or not. An id that is not a UUID is 400 `validation.failed`.\n\n**Required scope:** `organizations.read`",
        "operationId": "getOrganization",
        "parameters": [
          {
            "name": "organizationId",
            "in": "path",
            "description": "The organization's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                },
                "example": {
                  "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                  "name": "Spillard Logistics",
                  "supportEmail": "support@example.com",
                  "supportPhone": "+44 20 7946 0000",
                  "timeZone": "Europe/London"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `organization.not_found`: The organization does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "organization.not_found": {
                    "$ref": "#/components/examples/organization.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "organizations.read"
            ]
          }
        ]
      }
    },
    "/v1/status": {
      "get": {
        "tags": [
          "Status"
        ],
        "summary": "Get the service status",
        "description": "Returns `status: \"ok\"`, the version of the API build, the environment name and the server time (UTC, ISO 8601). It reads no fleet data, so it suits uptime checks. It needs a valid token but no scope.",
        "operationId": "getStatus",
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiStatus"
                },
                "example": {
                  "status": "ok",
                  "version": "1.0.0",
                  "environment": "Production",
                  "serverTime": "2026-09-29T08:05:00.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token's access list is damaged (`auth.tenant_forbidden`); ask Spillard to check the API client.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [ ]
          }
        ]
      }
    },
    "/v1/vehicles": {
      "get": {
        "tags": [
          "Vehicles"
        ],
        "summary": "List vehicles",
        "description": "Lists the vehicles your client may read, with their fleet, driver, devices and connectivity, ordered by `id`.\n\n- The filters combine: a vehicle must match all of them. `organizationIds`, `fleetIds` and `vehicleIds` only narrow the list; an id your client may not read matches nothing (never a 403).\n- `registrationNumber`, `chassisNumber`, `make` and `model` match the whole value. Use `search` for part of a registration number, hardware id or SIM number; it is at most 100 characters (400 `validation.failed` otherwise).\n- Page with `limit` and `cursor`; `count=true` adds `totalCount`, but is 400 `validation.failed` with `connectivityStatus` or `lastReportedFrom`.\n\n**Required scope:** `vehicles.read`",
        "operationId": "listVehicles",
        "parameters": [
          {
            "name": "organizationId",
            "in": "query",
            "description": "Only the data of this organization and, unless `includeSubOrganizations=false`, of the organizations below it (sub-organizations) that your client may read. It must be an organization your client may read (see `access` in `GET /v1/context`; an organization below one granted with its sub-organizations counts), otherwise 403 `auth.tenant_forbidden`. For an organization your client may read for some fleets only, only those fleets' data is returned.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "organizationIds",
            "in": "query",
            "description": "Only the data of these organizations. Ids outside the organizations your client may read match nothing (never a 403). For an organization your client may read for some fleets only, only those fleets' data is returned. Applied after `organizationId` and `includeSubOrganizations`: it narrows their result and never adds the organizations below an id.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "includeSubOrganizations",
            "in": "query",
            "description": "Whether sub-organizations are included: the organizations below `organizationId` (or, without it, below the organizations your client was granted); default true. With `organizationId`: true returns that organization and the organizations below it that your client may read, false that organization only. Without `organizationId`: true returns everything your client may read, false only the organizations your client was granted, without the organizations below them. It never widens what your client may read. `organizationIds` applies afterwards: it narrows the result to the organizations it lists and never adds the organizations below an id.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "registrationNumber",
            "in": "query",
            "description": "Only the vehicle with exactly this registration number.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "chassisNumber",
            "in": "query",
            "description": "Only vehicles with exactly this chassis number (VIN).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "make",
            "in": "query",
            "description": "Only vehicles of exactly this make.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "model",
            "in": "query",
            "description": "Only vehicles of exactly this model.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fleetIds",
            "in": "query",
            "description": "Only the data of these fleets (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "vehicleIds",
            "in": "query",
            "description": "Only these vehicles (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Only vehicles whose registration number, or the hardware id or SIM number of one of their devices, contains this text (case-insensitive; at most 100 characters). `registrationNumber` matches exactly.",
            "schema": {
              "maxLength": 100,
              "type": "string"
            }
          },
          {
            "name": "connectivityStatus",
            "in": "query",
            "description": "Only vehicles whose connectivity (that of their most recently reporting device) is one of these statuses (repeat the parameter for several, at most 100 values); a vehicle without a device never matches. Connectivity comes from live data: a filtered list is 503 `dependency.unavailable` while that data cannot be read. A filtered page may hold fewer items than `limit` while `hasMore` is true; follow `nextCursor`. `count=true` is 400 `validation.failed` with this filter.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/ConnectivityStatus"
              }
            }
          },
          {
            "name": "lastReportedFrom",
            "in": "query",
            "description": "Only vehicles with a device that last reported at or after this instant (UTC, ISO 8601). A vehicle whose devices never reported never matches. Uses the same live data as `connectivityStatus`. `count=true` is 400 `validation.failed` with it.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Count"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VehiclePage"
                },
                "example": {
                  "items": [
                    {
                      "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                      "registrationNumber": "SL24 WHK",
                      "make": "Ford",
                      "model": "Transit",
                      "chassisNumber": "WF0XXXTTGXPA12345",
                      "yearOfManufacture": 2024,
                      "enteredMileage": 15000,
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "driver": {
                        "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                      },
                      "devices": [
                        {
                          "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                          "hardwareId": "009a2c41f7",
                          "enabled": true,
                          "connectivityStatus": "online",
                          "lastReportedAt": "2026-09-29T08:02:40.000Z",
                          "firstReportedAt": "2025-03-14T09:12:05.000Z"
                        }
                      ],
                      "connectivityStatus": "online",
                      "lastReportedAt": "2026-09-29T08:02:40.000Z",
                      "firstReportedAt": "2025-03-14T09:12:05.000Z"
                    }
                  ],
                  "hasMore": true,
                  "nextCursor": "eyJ0IjoiMjAyNjA5MjlUMDc0MTEyIn0"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Create a vehicle",
        "description": "Creates a vehicle in `fleetId` and returns it with its `ETag`.\n\n- `registrationNumber`, `make` and `model` are required. The registration number must be unique in the fleet's organization (400 `validation.failed` otherwise). An invalid field is 400 `validation.failed`; `errors` names each one.\n- A `fleetId` that does not exist or that your client may not read is 422 `validation.reference_not_found`.\n- Send an `Idempotency-Key` to retry safely: within 24 hours a retry with the same key and body returns the first result.\n\n**Required scope:** `vehicles.write`",
        "operationId": "createVehicle",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Optional. A unique value per logical request (for example a UUID). A retry with the same key and body within 24 hours returns the first result instead of acting twice; the same key with another body is 409 `idempotency.key_conflict`. While the first request is still running the answer is 409 `idempotency.in_progress`: wait, then repeat the same request with the same key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateVehicleRequest"
              },
              "example": {
                "registrationNumber": "SL24 WHK",
                "fleetId": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35",
                "make": "Ford",
                "model": "Transit",
                "chassisNumber": "WF0XXXTTGXPA12345",
                "fuelType": "diesel",
                "vehicleType": "van",
                "engineSizeLiters": 2.0,
                "yearOfManufacture": 2024,
                "enteredMileage": 15000
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "ETag": {
                "description": "The version of the resource. Send it back in `If-Match` when you update the resource.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                },
                "example": {
                  "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                  "registrationNumber": "SL24 WHK",
                  "make": "Ford",
                  "model": "Transit",
                  "chassisNumber": "WF0XXXTTGXPA12345",
                  "fuelType": "diesel",
                  "vehicleType": "van",
                  "engineSizeLiters": 2.0,
                  "yearOfManufacture": 2024,
                  "enteredMileage": 15000,
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "driver": {
                    "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                  },
                  "trackingDevice": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "mediaDevice": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "devices": [
                    {
                      "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                      "hardwareId": "009a2c41f7",
                      "enabled": true,
                      "connectivityStatus": "online",
                      "lastReportedAt": "2026-09-29T08:02:40.000Z",
                      "firstReportedAt": "2025-03-14T09:12:05.000Z"
                    }
                  ],
                  "connectivityStatus": "online",
                  "lastReportedAt": "2026-09-29T08:02:40.000Z",
                  "firstReportedAt": "2025-03-14T09:12:05.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `idempotency.in_progress`: The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.\n- `idempotency.key_conflict`: The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "idempotency.in_progress": {
                    "$ref": "#/components/examples/idempotency.in_progress"
                  },
                  "idempotency.key_conflict": {
                    "$ref": "#/components/examples/idempotency.key_conflict"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Entity. `errorCode`:\n\n- `validation.reference_not_found`: An id in the body (for example `fleetId`) does not exist or is outside your client's access. Send an id your client may read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.reference_not_found": {
                    "$ref": "#/components/examples/validation.reference_not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.write"
            ]
          }
        ]
      }
    },
    "/v1/vehicles/{vehicleId}": {
      "get": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Get a vehicle",
        "description": "Returns the vehicle with its driver, devices and connectivity. The `ETag` header is the value to send in `If-Match` when you update it.\n\n**Required scope:** `vehicles.read`",
        "operationId": "getVehicle",
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "description": "The vehicle's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "ETag": {
                "description": "The version of the resource. Send it back in `If-Match` when you update the resource.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                },
                "example": {
                  "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                  "registrationNumber": "SL24 WHK",
                  "make": "Ford",
                  "model": "Transit",
                  "chassisNumber": "WF0XXXTTGXPA12345",
                  "fuelType": "diesel",
                  "vehicleType": "van",
                  "engineSizeLiters": 2.0,
                  "yearOfManufacture": 2024,
                  "enteredMileage": 15000,
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "driver": {
                    "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                  },
                  "trackingDevice": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "mediaDevice": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "devices": [
                    {
                      "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                      "hardwareId": "009a2c41f7",
                      "enabled": true,
                      "connectivityStatus": "online",
                      "lastReportedAt": "2026-09-29T08:02:40.000Z",
                      "firstReportedAt": "2025-03-14T09:12:05.000Z"
                    }
                  ],
                  "connectivityStatus": "online",
                  "lastReportedAt": "2026-09-29T08:02:40.000Z",
                  "firstReportedAt": "2025-03-14T09:12:05.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `vehicle.not_found`: The vehicle does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "vehicle.not_found": {
                    "$ref": "#/components/examples/vehicle.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.read"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Update a vehicle",
        "description": "Changes only the fields you send; a required text cannot be blanked. Returns the vehicle with its new `ETag`.\n\n- Send `If-Match` with the `ETag` of your last read: 428 `precondition.required` without it, 412 `concurrency.etag_mismatch` when the vehicle changed since. Read it again, then retry.\n- A registration number that another vehicle of the organization uses is 400 `validation.failed`.\n\n**Required scope:** `vehicles.write`",
        "operationId": "updateVehicle",
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "description": "The vehicle's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "If-Match",
            "in": "header",
            "description": "The `ETag` of your last read of the vehicle. Required: 428 `precondition.required` without it, 412 `concurrency.etag_mismatch` when the vehicle changed since.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateVehicleRequest"
              },
              "example": {
                "model": "Transit Custom",
                "enteredMileage": 18250
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "ETag": {
                "description": "The version of the resource. Send it back in `If-Match` when you update the resource.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                },
                "example": {
                  "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                  "registrationNumber": "SL24 WHK",
                  "make": "Ford",
                  "model": "Transit Custom",
                  "chassisNumber": "WF0XXXTTGXPA12345",
                  "fuelType": "diesel",
                  "vehicleType": "van",
                  "engineSizeLiters": 2.0,
                  "yearOfManufacture": 2024,
                  "enteredMileage": 18250,
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "driver": {
                    "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                  },
                  "trackingDevice": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "mediaDevice": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "devices": [
                    {
                      "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                      "hardwareId": "009a2c41f7",
                      "enabled": true,
                      "connectivityStatus": "online",
                      "lastReportedAt": "2026-09-29T08:02:40.000Z",
                      "firstReportedAt": "2025-03-14T09:12:05.000Z"
                    }
                  ],
                  "connectivityStatus": "online",
                  "lastReportedAt": "2026-09-29T08:02:40.000Z",
                  "firstReportedAt": "2025-03-14T09:12:05.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `vehicle.not_found`: The vehicle does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "vehicle.not_found": {
                    "$ref": "#/components/examples/vehicle.not_found"
                  }
                }
              }
            }
          },
          "412": {
            "description": "Precondition Failed. `errorCode`:\n\n- `concurrency.etag_mismatch`: The resource changed since you read it: `If-Match` no longer matches its `ETag`. Read it again, then retry the update.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "concurrency.etag_mismatch": {
                    "$ref": "#/components/examples/concurrency.etag_mismatch"
                  }
                }
              }
            }
          },
          "428": {
            "description": "Precondition Required. `errorCode`:\n\n- `precondition.required`: A required header is missing: `If-Match` when you update a vehicle or a driver, `Idempotency-Key` when you create a media request. Send it and retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "precondition.required": {
                    "$ref": "#/components/examples/precondition.required"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.write"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Delete a vehicle",
        "description": "Deletes the vehicle: it is no longer listed, and reading it is 404 `vehicle.not_found`.\n\n**Required scope:** `vehicles.write`",
        "operationId": "deleteVehicle",
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "description": "The vehicle's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `vehicle.not_found`: The vehicle does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "vehicle.not_found": {
                    "$ref": "#/components/examples/vehicle.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.write"
            ]
          }
        ]
      }
    },
    "/v1/vehicle-positions": {
      "get": {
        "tags": [
          "Vehicles"
        ],
        "summary": "List the latest vehicle positions",
        "description": "Lists the latest position of each vehicle your client may read that has one, ordered by vehicle `id`. A vehicle without a tracking device or a position is left out.\n\n- A page can hold fewer items than `limit` while `hasMore` is true: follow `nextCursor`.\n- The list takes no `count` and has no `totalCount`; to count vehicles, use `GET /v1/vehicles?count=true`.\n- `vehicleIds` narrows the list to these vehicles. To read the positions of the vehicles that are online, take their ids from `GET /v1/vehicles?connectivityStatus=online`.\n- `connectivityStatus` is computed from `recordedAt`: `online` within 2 minutes, `offline` up to 72 hours, `sleep` after that. It is the status of the tracking device, so it can differ from the vehicle's when another device reported more recently. The Spillard web app calls `sleep` Inactive.\n- For positions as they happen, subscribe to the `spillard.telemetry.live.v1` webhook instead of polling.\n\n**Required scope:** `vehicles.read`",
        "operationId": "listVehiclePositions",
        "parameters": [
          {
            "name": "fleetIds",
            "in": "query",
            "description": "Only the data of these fleets (repeat the parameter for several, at most 100 values).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "vehicleIds",
            "in": "query",
            "description": "Only the positions of these vehicles (repeat the parameter for several, at most 100 values). An id that is not a vehicle your client may read matches nothing (never a 403).",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "Only the data of this organization and, unless `includeSubOrganizations=false`, of the organizations below it (sub-organizations) that your client may read. It must be an organization your client may read (see `access` in `GET /v1/context`; an organization below one granted with its sub-organizations counts), otherwise 403 `auth.tenant_forbidden`. For an organization your client may read for some fleets only, only those fleets' data is returned.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "organizationIds",
            "in": "query",
            "description": "Only the data of these organizations. Ids outside the organizations your client may read match nothing (never a 403). For an organization your client may read for some fleets only, only those fleets' data is returned. Applied after `organizationId` and `includeSubOrganizations`: it narrows their result and never adds the organizations below an id.",
            "schema": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "includeSubOrganizations",
            "in": "query",
            "description": "Whether sub-organizations are included: the organizations below `organizationId` (or, without it, below the organizations your client was granted); default true. With `organizationId`: true returns that organization and the organizations below it that your client may read, false that organization only. Without `organizationId`: true returns everything your client may read, false only the organizations your client was granted, without the organizations below them. It never widens what your client may read. `organizationIds` applies afterwards: it narrows the result to the organizations it lists and never adds the organizations below an id.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VehiclePositionPage"
                },
                "example": {
                  "items": [
                    {
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                        "registrationNumber": "SL24 WHK",
                        "chassisNumber": "WF0XXXTTGXPA12345"
                      },
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "driver": {
                        "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                      },
                      "device": {
                        "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                        "hardwareId": "009a2c41f7"
                      },
                      "location": {
                        "latitude": 51.481022,
                        "longitude": -0.429587
                      },
                      "gpsValid": true,
                      "speedKph": 0,
                      "headingDegrees": 270,
                      "recordedAt": "2026-09-29T08:02:40.000Z",
                      "connectivityStatus": "online"
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.read"
            ]
          }
        ]
      }
    },
    "/v1/vehicles/{vehicleId}/position": {
      "get": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Get a vehicle's latest position",
        "description": "Returns the latest position from the vehicle's tracking device; 204 (no body) when the vehicle has no tracking device or the device has not reported a position yet. Check `gpsValid` before you use `location`.\n\n**Required scope:** `vehicles.read`",
        "operationId": "getVehiclePosition",
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "description": "The vehicle's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VehiclePosition"
                },
                "example": {
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                    "registrationNumber": "SL24 WHK",
                    "chassisNumber": "WF0XXXTTGXPA12345"
                  },
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "driver": {
                    "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                  },
                  "device": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "location": {
                    "latitude": 51.481022,
                    "longitude": -0.429587
                  },
                  "gpsValid": true,
                  "speedKph": 0,
                  "headingDegrees": 270,
                  "recordedAt": "2026-09-29T08:02:40.000Z",
                  "connectivityStatus": "online"
                }
              }
            }
          },
          "204": {
            "description": "No Content",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `vehicle.not_found`: The vehicle does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "vehicle.not_found": {
                    "$ref": "#/components/examples/vehicle.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.read"
            ]
          }
        ]
      }
    },
    "/v1/vehicles/{vehicleId}/drivers/{driverId}": {
      "put": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Assign a driver to a vehicle",
        "description": "Makes the driver the vehicle's driver. Assigning the same driver again changes nothing (204).\n\n- The vehicle and the driver must be ones your client may read (404 `vehicle.not_found` or `driver.not_found`) and in the same fleet (422 `vehicle.driver_fleet_mismatch`).\n- 409 `resource.link_conflict` when the vehicle already has another driver (unassign that one first), or the driver is assigned to another vehicle: a driver drives one vehicle.\n\n**Required scope:** `vehicles.write`",
        "operationId": "assignVehicleDriver",
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "description": "The vehicle's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "driverId",
            "in": "path",
            "description": "The driver's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `driver.not_found`: The driver does not exist or is outside your client's access. Check the id. On a vehicle route it can also mean the driver does not drive that vehicle.\n- `vehicle.not_found`: The vehicle does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "driver.not_found": {
                    "$ref": "#/components/examples/driver.not_found"
                  },
                  "vehicle.not_found": {
                    "$ref": "#/components/examples/vehicle.not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `resource.link_conflict`: The link cannot be made as asked: the vehicle already has a driver, the driver or device is linked to another vehicle, the two are in different organizations or fleets, or the registration number is taken in the target organization. Unassign the other link or change the target first.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "resource.link_conflict": {
                    "$ref": "#/components/examples/resource.link_conflict"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Entity. `errorCode`:\n\n- `vehicle.driver_fleet_mismatch`: The vehicle and the driver are in different fleets. Use a driver of the vehicle's fleet, or move the vehicle first.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "vehicle.driver_fleet_mismatch": {
                    "$ref": "#/components/examples/vehicle.driver_fleet_mismatch"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.write"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Unassign a driver from a vehicle",
        "description": "Removes the driver from the vehicle; the driver is kept. A driver who is not the vehicle's driver is 404 `driver.not_found`.\n\n**Required scope:** `vehicles.write`",
        "operationId": "unassignVehicleDriver",
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "description": "The vehicle's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "driverId",
            "in": "path",
            "description": "The driver's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `driver.not_found`: The driver does not exist or is outside your client's access. Check the id. On a vehicle route it can also mean the driver does not drive that vehicle.\n- `vehicle.not_found`: The vehicle does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "driver.not_found": {
                    "$ref": "#/components/examples/driver.not_found"
                  },
                  "vehicle.not_found": {
                    "$ref": "#/components/examples/vehicle.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.write"
            ]
          }
        ]
      }
    },
    "/v1/vehicles/{vehicleId}/devices/{deviceId}": {
      "put": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Assign a device to a vehicle",
        "description": "Fits the device to the vehicle. Assigning it again changes nothing (204).\n\n- The device must be in the vehicle's organization and fleet (409 `resource.link_conflict` otherwise).\n- A device fitted to another vehicle is 409 `resource.link_conflict`: unassign it there first.\n\n**Required scope:** `vehicles.write`",
        "operationId": "assignVehicleDevice",
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "description": "The vehicle's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "deviceId",
            "in": "path",
            "description": "The device's id (a UUID) or its hardware id. A UUID is looked up as a device id first; a value that is not the id of one of your devices is looked up as a hardware id (case-insensitive).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device.not_found`: The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.\n- `vehicle.not_found`: The vehicle does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device.not_found": {
                    "$ref": "#/components/examples/device.not_found"
                  },
                  "vehicle.not_found": {
                    "$ref": "#/components/examples/vehicle.not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `resource.link_conflict`: The link cannot be made as asked: the vehicle already has a driver, the driver or device is linked to another vehicle, the two are in different organizations or fleets, or the registration number is taken in the target organization. Unassign the other link or change the target first.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "resource.link_conflict": {
                    "$ref": "#/components/examples/resource.link_conflict"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.write"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Unassign a device from a vehicle",
        "description": "Removes the device from the vehicle; the device stays in its fleet. A device that is not fitted to this vehicle is 404 `device.not_found`.\n\n**Required scope:** `vehicles.write`",
        "operationId": "unassignVehicleDevice",
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "description": "The vehicle's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "deviceId",
            "in": "path",
            "description": "The device's id (a UUID) or its hardware id. A UUID is looked up as a device id first; a value that is not the id of one of your devices is looked up as a hardware id (case-insensitive).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `device.not_found`: The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.\n- `vehicle.not_found`: The vehicle does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "device.not_found": {
                    "$ref": "#/components/examples/device.not_found"
                  },
                  "vehicle.not_found": {
                    "$ref": "#/components/examples/vehicle.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.write"
            ]
          }
        ]
      }
    },
    "/v1/vehicles/{vehicleId}:move": {
      "post": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Move a vehicle to another fleet",
        "description": "Moves the vehicle to the fleet `fleetId` with its driver and devices, and returns it with its `ETag`. The vehicle takes the fleet's organization, which may be another organization your client may read.\n\n- The vehicle, its driver and every device of the vehicle must be ones your client may read (404 `vehicle.not_found` otherwise), and so must the target fleet (422 `validation.reference_not_found`).\n- A vehicle already in the fleet is 409 `vehicle.already_in_fleet`; a target organization that has a vehicle with the same registration number is 409 `resource.link_conflict`.\n- Events, journeys and media keep the organization and fleet they were recorded in.\n- Send an `Idempotency-Key` to retry safely; it is optional.\n\n**Required scope:** `vehicles.write`",
        "operationId": "moveVehicle",
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "description": "The vehicle's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Optional. A unique value per logical request (for example a UUID). A retry with the same key and body within 24 hours returns the first result instead of acting twice; the same key with another body is 409 `idempotency.key_conflict`. While the first request is still running the answer is 409 `idempotency.in_progress`: wait, then repeat the same request with the same key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoveVehicleRequest"
              },
              "example": {
                "fleetId": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "ETag": {
                "description": "The version of the resource. Send it back in `If-Match` when you update the resource.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                },
                "example": {
                  "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                  "registrationNumber": "SL24 WHK",
                  "make": "Ford",
                  "model": "Transit",
                  "chassisNumber": "WF0XXXTTGXPA12345",
                  "fuelType": "diesel",
                  "vehicleType": "van",
                  "engineSizeLiters": 2.0,
                  "yearOfManufacture": 2024,
                  "enteredMileage": 15000,
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80"
                  },
                  "driver": {
                    "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                  },
                  "trackingDevice": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "mediaDevice": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "devices": [
                    {
                      "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                      "hardwareId": "009a2c41f7",
                      "enabled": true,
                      "connectivityStatus": "online",
                      "lastReportedAt": "2026-09-29T08:02:40.000Z",
                      "firstReportedAt": "2025-03-14T09:12:05.000Z"
                    }
                  ],
                  "connectivityStatus": "online",
                  "lastReportedAt": "2026-09-29T08:02:40.000Z",
                  "firstReportedAt": "2025-03-14T09:12:05.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `vehicle.not_found`: The vehicle does not exist or is outside your client's access. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "vehicle.not_found": {
                    "$ref": "#/components/examples/vehicle.not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `idempotency.in_progress`: The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.\n- `idempotency.key_conflict`: The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.\n- `resource.link_conflict`: The link cannot be made as asked: the vehicle already has a driver, the driver or device is linked to another vehicle, the two are in different organizations or fleets, or the registration number is taken in the target organization. Unassign the other link or change the target first.\n- `vehicle.already_in_fleet`: The vehicle is in the target fleet already, so nothing would change. Choose another fleet.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "idempotency.in_progress": {
                    "$ref": "#/components/examples/idempotency.in_progress"
                  },
                  "idempotency.key_conflict": {
                    "$ref": "#/components/examples/idempotency.key_conflict"
                  },
                  "resource.link_conflict": {
                    "$ref": "#/components/examples/resource.link_conflict"
                  },
                  "vehicle.already_in_fleet": {
                    "$ref": "#/components/examples/vehicle.already_in_fleet"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Entity. `errorCode`:\n\n- `validation.reference_not_found`: An id in the body (for example `fleetId`) does not exist or is outside your client's access. Send an id your client may read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.reference_not_found": {
                    "$ref": "#/components/examples/validation.reference_not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.write"
            ]
          }
        ]
      }
    },
    "/v1/vehicles:batchMove": {
      "post": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Move several vehicles",
        "description": "Moves 1 to 100 vehicles, each with its driver and devices, by the rules of `POST /v1/vehicles/{vehicleId}:move`. All the moves are made, or none.\n\n- The body is `{\"items\": [{\"vehicleId\": \"...\", \"fleetId\": \"...\"}]}`. The vehicle takes the fleet's organization.\n- Every move is checked first. When one is refused, none is made: `results` gives the `errorCode` of each refused move and `batch.aborted` for the others.\n- The answer is 200 either way: read `moved`.\n- A vehicle twice in one batch, or a list of fewer than 1 or more than 100 moves, is 400 `validation.failed`.\n- A driver cannot go to two fleets in one batch (409 `resource.link_conflict` in `results`).\n- `Idempotency-Key` is optional.\n\n**Required scope:** `vehicles.write`",
        "operationId": "batchMoveVehicles",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Optional. A unique value per logical request (for example a UUID). A retry with the same key and body within 24 hours returns the first result instead of acting twice; the same key with another body is 409 `idempotency.key_conflict`. While the first request is still running the answer is 409 `idempotency.in_progress`: wait, then repeat the same request with the same key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchMoveVehiclesRequest"
              },
              "example": {
                "items": [
                  {
                    "vehicleId": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                    "fleetId": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80"
                  },
                  {
                    "vehicleId": "e2a7c5d1-9f43-4b86-8a0e-3c6b1d7f9a24",
                    "fleetId": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80"
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchMoveVehiclesResult"
                },
                "example": {
                  "moved": true,
                  "processedCount": 2,
                  "failedCount": 0,
                  "results": [
                    {
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041"
                      },
                      "fleet": {
                        "id": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80"
                      },
                      "moved": true,
                      "detail": null,
                      "errorCode": null
                    },
                    {
                      "vehicle": {
                        "id": "e2a7c5d1-9f43-4b86-8a0e-3c6b1d7f9a24"
                      },
                      "fleet": {
                        "id": "5d8e0b47-21c3-4f9a-b6e2-7a1c9d3f5e80"
                      },
                      "moved": true,
                      "detail": null,
                      "errorCode": null
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `idempotency.in_progress`: The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.\n- `idempotency.key_conflict`: The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "idempotency.in_progress": {
                    "$ref": "#/components/examples/idempotency.in_progress"
                  },
                  "idempotency.key_conflict": {
                    "$ref": "#/components/examples/idempotency.key_conflict"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "vehicles.write"
            ]
          }
        ]
      }
    },
    "/v1/webhook-endpoints": {
      "get": {
        "tags": [
          "WebhookEndpoints"
        ],
        "summary": "List webhook endpoints",
        "description": "Lists the organization's webhook endpoints, oldest first, with their webhook types (`eventTypes`), device event filters (`eventTypeFilters`) and secret hints. An organization has a limited number of endpoints (`endpointLimit` of `GET /v1/webhook-settings`). Signing secrets are never returned.\n\n**Required scope:** `webhooks.read`",
        "operationId": "listWebhookEndpoints",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/ManagementLimit"
          },
          {
            "$ref": "#/components/parameters/Count"
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "The organization whose webhooks to manage: your client's organization when omitted, or another organization your client was granted with every fleet (an entry of `access` in `GET /v1/context` with `allFleets: true`; not an organization below it). An organization your client may read for some fleets only is 403 `auth.fleet_restricted`: its webhooks serve every fleet. Any other organization is 403 `auth.tenant_forbidden`. Endpoints belong to their organization; send the same `organizationId` on every call about an endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointPage"
                },
                "example": {
                  "items": [
                    {
                      "id": "4b9e2d71-8c5a-4f3e-9d16-a7c0e5b2f849",
                      "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                      "name": "Events to the claims system",
                      "url": "https://hooks.example.com/spillard",
                      "eventTypes": [
                        "spillard.event.raised.v1",
                        "spillard.media.request.completed.v1"
                      ],
                      "eventTypeFilters": [
                        "driver_behaviour.*",
                        "safety.panic_button"
                      ],
                      "active": true,
                      "includeSubOrganizations": true,
                      "createdAt": "2026-09-29T08:10:00.000Z",
                      "updatedAt": "2026-09-29T08:10:00.000Z",
                      "secretHint": "****************************************MDE=",
                      "previousSecretExpiresAt": null,
                      "previousSecretHint": null
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "webhooks.read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "WebhookEndpoints"
        ],
        "summary": "Create a webhook endpoint",
        "description": "Subscribes an HTTPS endpoint to one or more webhook types and returns its signing secret, once.\n\n- **Fields.** `name` is unique in the organization (1 to 64 characters); `url` must be a public HTTPS address; `eventTypes` needs at least one webhook type. An invalid field is 400 `validation.failed`.\n- **Types.** `eventTypes` holds webhook types (`spillard.*.v1`, for example `spillard.event.raised.v1`), not device event types. Each webhook type can be subscribed by one active endpoint of the organization (409 `webhook_endpoint.conflict` otherwise). An organization has at most `endpointLimit` endpoints (10 by default, see `GET /v1/webhook-settings`; 409 `webhook_endpoint.limit_exceeded`).\n- **Filters.** `eventTypeFilters` holds device event types (for example `adas.*`, `dsm.fatigue`). It narrows the `spillard.event.raised.v1` messages only, and is allowed only when the endpoint subscribes to that type.\n- **Sub-organizations.** `includeSubOrganizations` makes the endpoint also receive the events of every organization below the endpoint's organization, at any depth, in addition to their own endpoints. Those messages name the sub-organization in `source`. When omitted it is true if your client may read the sub-organizations of the endpoint's organization (that organization's entry in `access` of `GET /v1/context` has `includeSubOrganizations: true`), otherwise false; `true` without that permission is 403 `auth.tenant_forbidden`.\n- **Delivery.** Messages go out only while the organization's webhook delivery is on, and creating an endpoint does not turn it on: check the endpoint with `:test`, then call `PUT /v1/webhook-settings` with `{\"enabled\": true}`. Each message is signed (`webhook-signature`) and must be answered with 2xx within 10 seconds. We recommend that your receiver also rejects a message whose `webhook-timestamp` is more than 5 minutes from its clock; every attempt carries a new timestamp. A failure is retried with backoff for up to 24 hours (72 hours for `spillard.telemetry.history.v1`, 60 seconds for `spillard.telemetry.live.v1`); a response other than 2xx, 408, 429 or 5xx is not retried. Deduplicate on `webhook-id`.\n- **Retry.** No `Idempotency-Key`: the signing secret is never stored for a replay. A retry after a lost response is 409 `webhook_endpoint.conflict` (the name is taken): find the endpoint with `GET /v1/webhook-endpoints` and issue a new secret with `:rotate-secret`.\n\n**Required scope:** `webhooks.write`",
        "operationId": "createWebhookEndpoint",
        "parameters": [
          {
            "name": "organizationId",
            "in": "query",
            "description": "The organization whose webhooks to manage: your client's organization when omitted, or another organization your client was granted with every fleet (an entry of `access` in `GET /v1/context` with `allFleets: true`; not an organization below it). An organization your client may read for some fleets only is 403 `auth.fleet_restricted`: its webhooks serve every fleet. Any other organization is 403 `auth.tenant_forbidden`. Endpoints belong to their organization; send the same `organizationId` on every call about an endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookEndpointRequest"
              },
              "example": {
                "name": "Events to the claims system",
                "url": "https://hooks.example.com/spillard",
                "eventTypes": [
                  "spillard.event.raised.v1",
                  "spillard.media.request.completed.v1"
                ],
                "eventTypeFilters": [
                  "driver_behaviour.*",
                  "safety.panic_button"
                ],
                "active": true
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "Location": {
                "description": "The path of the new endpoint (`/v1/webhook-endpoints/{endpointId}`, with `?organizationId=` when the request named an organization).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointCreated"
                },
                "example": {
                  "id": "4b9e2d71-8c5a-4f3e-9d16-a7c0e5b2f849",
                  "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                  "name": "Events to the claims system",
                  "url": "https://hooks.example.com/spillard",
                  "eventTypes": [
                    "spillard.event.raised.v1",
                    "spillard.media.request.completed.v1"
                  ],
                  "eventTypeFilters": [
                    "driver_behaviour.*",
                    "safety.panic_button"
                  ],
                  "active": true,
                  "includeSubOrganizations": true,
                  "createdAt": "2026-09-29T08:10:00.000Z",
                  "signingSecret": "whsec_c3BpbGxhcmQtZXhhbXBsZS1zZWNyZXQtMDE=",
                  "secretHint": "****************************************MDE="
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `webhook_endpoint.conflict`: The endpoint name is taken, a webhook type is subscribed by another active endpoint, or the endpoint has no usable signing secret. Change the name or the types, or issue a secret with `:rotate-secret`.\n- `webhook_endpoint.limit_exceeded`: The organization has the maximum number of webhook endpoints (`endpointLimit` in `GET /v1/webhook-settings`) already. Delete an endpoint first.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "webhook_endpoint.conflict": {
                    "$ref": "#/components/examples/webhook_endpoint.conflict"
                  },
                  "webhook_endpoint.limit_exceeded": {
                    "$ref": "#/components/examples/webhook_endpoint.limit_exceeded"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "webhooks.write"
            ]
          }
        ]
      }
    },
    "/v1/webhook-endpoints/{endpointId}": {
      "get": {
        "tags": [
          "WebhookEndpoints"
        ],
        "summary": "Get a webhook endpoint",
        "description": "Returns one endpoint with its webhook types, device event filters and secret hints, never its signing secret. An id that is not an endpoint of the organization is 404 `webhook_endpoint.not_found`.\n\n**Required scope:** `webhooks.read`",
        "operationId": "getWebhookEndpoint",
        "parameters": [
          {
            "name": "endpointId",
            "in": "path",
            "description": "The webhook endpoint's id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "The organization whose webhooks to manage: your client's organization when omitted, or another organization your client was granted with every fleet (an entry of `access` in `GET /v1/context` with `allFleets: true`; not an organization below it). An organization your client may read for some fleets only is 403 `auth.fleet_restricted`: its webhooks serve every fleet. Any other organization is 403 `auth.tenant_forbidden`. Endpoints belong to their organization; send the same `organizationId` on every call about an endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                },
                "example": {
                  "id": "4b9e2d71-8c5a-4f3e-9d16-a7c0e5b2f849",
                  "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                  "name": "Events to the claims system",
                  "url": "https://hooks.example.com/spillard",
                  "eventTypes": [
                    "spillard.event.raised.v1",
                    "spillard.media.request.completed.v1"
                  ],
                  "eventTypeFilters": [
                    "driver_behaviour.*",
                    "safety.panic_button"
                  ],
                  "active": true,
                  "includeSubOrganizations": true,
                  "createdAt": "2026-09-29T08:10:00.000Z",
                  "updatedAt": "2026-09-29T08:10:00.000Z",
                  "secretHint": "****************************************MDE=",
                  "previousSecretExpiresAt": null,
                  "previousSecretHint": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `webhook_endpoint.not_found`: No webhook endpoint with this id belongs to the organization. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "webhook_endpoint.not_found": {
                    "$ref": "#/components/examples/webhook_endpoint.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "webhooks.read"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "WebhookEndpoints"
        ],
        "summary": "Update a webhook endpoint",
        "description": "Changes only the fields you send. `eventTypes` holds webhook types, `eventTypeFilters` device event types; an empty `eventTypeFilters` clears the filter. The rules of `POST /v1/webhook-endpoints` apply (409 `webhook_endpoint.conflict` for a name or type already taken).\n\n- An omitted `includeSubOrganizations` keeps the stored value, which an administrator may also have set in Spillard. `false` stops the deliveries of sub-organization events within about a minute; their pending retries end as `failed` with `failureReason` `disabled`.\n- A client that may not read the sub-organizations of the endpoint's organization (that organization's entry in `access` of `GET /v1/context` has `includeSubOrganizations: false`) gets 403 `auth.tenant_forbidden` for `includeSubOrganizations: true`, also on an endpoint that has it on already: omit the field to keep the stored value.\n- On an endpoint that has it on, such a client also gets 403 `auth.tenant_forbidden` for a new `url`, an added type, a wider `eventTypeFilters` or `active: true` on an inactive endpoint, unless the same request sends `includeSubOrganizations: false`. It may rename the endpoint, narrow its types or filters, set `active: false` or send `includeSubOrganizations: false`.\n\n**Required scope:** `webhooks.write`",
        "operationId": "updateWebhookEndpoint",
        "parameters": [
          {
            "name": "endpointId",
            "in": "path",
            "description": "The webhook endpoint's id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "The organization whose webhooks to manage: your client's organization when omitted, or another organization your client was granted with every fleet (an entry of `access` in `GET /v1/context` with `allFleets: true`; not an organization below it). An organization your client may read for some fleets only is 403 `auth.fleet_restricted`: its webhooks serve every fleet. Any other organization is 403 `auth.tenant_forbidden`. Endpoints belong to their organization; send the same `organizationId` on every call about an endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookEndpointRequest"
              },
              "example": {
                "eventTypeFilters": [
                  "driver_behaviour.*",
                  "adas.*"
                ],
                "active": true
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                },
                "example": {
                  "id": "4b9e2d71-8c5a-4f3e-9d16-a7c0e5b2f849",
                  "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                  "name": "Events to the claims system",
                  "url": "https://hooks.example.com/spillard",
                  "eventTypes": [
                    "spillard.event.raised.v1",
                    "spillard.media.request.completed.v1"
                  ],
                  "eventTypeFilters": [
                    "driver_behaviour.*",
                    "adas.*"
                  ],
                  "active": true,
                  "includeSubOrganizations": true,
                  "createdAt": "2026-09-29T08:10:00.000Z",
                  "updatedAt": "2026-09-29T08:30:00.000Z",
                  "secretHint": "****************************************MDE=",
                  "previousSecretExpiresAt": null,
                  "previousSecretHint": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `webhook_endpoint.not_found`: No webhook endpoint with this id belongs to the organization. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "webhook_endpoint.not_found": {
                    "$ref": "#/components/examples/webhook_endpoint.not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `webhook_endpoint.conflict`: The endpoint name is taken, a webhook type is subscribed by another active endpoint, or the endpoint has no usable signing secret. Change the name or the types, or issue a secret with `:rotate-secret`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "webhook_endpoint.conflict": {
                    "$ref": "#/components/examples/webhook_endpoint.conflict"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "webhooks.write"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "WebhookEndpoints"
        ],
        "summary": "Delete a webhook endpoint",
        "description": "Deletes the endpoint: nothing more is delivered to it.\n\n**Required scope:** `webhooks.write`",
        "operationId": "deleteWebhookEndpoint",
        "parameters": [
          {
            "name": "endpointId",
            "in": "path",
            "description": "The webhook endpoint's id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "The organization whose webhooks to manage: your client's organization when omitted, or another organization your client was granted with every fleet (an entry of `access` in `GET /v1/context` with `allFleets: true`; not an organization below it). An organization your client may read for some fleets only is 403 `auth.fleet_restricted`: its webhooks serve every fleet. Any other organization is 403 `auth.tenant_forbidden`. Endpoints belong to their organization; send the same `organizationId` on every call about an endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `webhook_endpoint.not_found`: No webhook endpoint with this id belongs to the organization. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "webhook_endpoint.not_found": {
                    "$ref": "#/components/examples/webhook_endpoint.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "webhooks.write"
            ]
          }
        ]
      }
    },
    "/v1/webhook-endpoints/{endpointId}:rotate-secret": {
      "post": {
        "tags": [
          "WebhookEndpoints"
        ],
        "summary": "Rotate a webhook signing secret",
        "description": "Issues a new signing secret, shown exactly once.\n\n- The new secret starts signing within about a minute; until then deliveries carry only the previous secret's signature.\n- From then until `previousSecretExpiresAt` (24 hours by default) `webhook-signature` carries two `v1` signatures, the new secret's first, and a delivery is valid if either verifies. Verify with every secret you hold until `previousSecretExpiresAt`; do not switch to the new secret at once.\n- Only the secret replaced by the latest rotation keeps signing: rotating again (for example after a lost response) stops the older secret within about a minute. There is no `Idempotency-Key`.\n\n**Required scope:** `webhooks.write`",
        "operationId": "rotateWebhookEndpointSecret",
        "parameters": [
          {
            "name": "endpointId",
            "in": "path",
            "description": "The webhook endpoint's id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "The organization whose webhooks to manage: your client's organization when omitted, or another organization your client was granted with every fleet (an entry of `access` in `GET /v1/context` with `allFleets: true`; not an organization below it). An organization your client may read for some fleets only is 403 `auth.fleet_restricted`: its webhooks serve every fleet. Any other organization is 403 `auth.tenant_forbidden`. Endpoints belong to their organization; send the same `organizationId` on every call about an endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointSecretRotated"
                },
                "example": {
                  "signingSecret": "whsec_c3BpbGxhcmQtZXhhbXBsZS1zZWNyZXQtMDI=",
                  "secretHint": "****************************************MDI=",
                  "previousSecretExpiresAt": "2026-09-30T08:15:00.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `webhook_endpoint.not_found`: No webhook endpoint with this id belongs to the organization. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "webhook_endpoint.not_found": {
                    "$ref": "#/components/examples/webhook_endpoint.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "webhooks.write"
            ]
          }
        ]
      }
    },
    "/v1/webhook-endpoints/{endpointId}:test": {
      "post": {
        "tags": [
          "WebhookEndpoints"
        ],
        "summary": "Send a test message",
        "description": "Sends one signed test message to the endpoint now and returns the outcome, so you can check connectivity and signature checks before you turn delivery on.\n\n- **The message.** The published example of the webhook type `eventType` (default: the endpoint's first type) with a new `id`, the current `time` and your organization as `source`; the other ids in `data` are illustrative. It is signed like every delivery and sent whatever the webhook settings and the endpoint's `active` flag, types, filters and `includeSubOrganizations` are.\n- **Once.** A test is never retried and is not in the delivery log.\n- **The answer.** `outcome` (`delivered`, `rejected`, `unreachable` or `blocked`), the receiver's status and the latency, never the receiver's response body.\n- **Errors.** An unknown `eventType` is 400 `validation.failed`; 409 `webhook_endpoint.conflict` when the endpoint has no usable signing secret: issue one with `:rotate-secret`.\n\n**Required scope:** `webhooks.write`",
        "operationId": "testWebhookEndpoint",
        "parameters": [
          {
            "name": "endpointId",
            "in": "path",
            "description": "The webhook endpoint's id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "The organization whose webhooks to manage: your client's organization when omitted, or another organization your client was granted with every fleet (an entry of `access` in `GET /v1/context` with `allFleets: true`; not an organization below it). An organization your client may read for some fleets only is 403 `auth.fleet_restricted`: its webhooks serve every fleet. Any other organization is 403 `auth.tenant_forbidden`. Endpoints belong to their organization; send the same `organizationId` on every call about an endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookTestRequest"
              },
              "example": {
                "eventType": "spillard.event.raised.v1"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResult"
                },
                "example": {
                  "deliveryId": "2f6b9e14-8a3c-5d70-b1e4-6c9a2d7f0b35",
                  "eventType": "spillard.event.raised.v1",
                  "outcome": "delivered",
                  "responseStatusCode": 204,
                  "latencyMilliseconds": 182
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `webhook_endpoint.not_found`: No webhook endpoint with this id belongs to the organization. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "webhook_endpoint.not_found": {
                    "$ref": "#/components/examples/webhook_endpoint.not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `webhook_endpoint.conflict`: The endpoint name is taken, a webhook type is subscribed by another active endpoint, or the endpoint has no usable signing secret. Change the name or the types, or issue a secret with `:rotate-secret`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "webhook_endpoint.conflict": {
                    "$ref": "#/components/examples/webhook_endpoint.conflict"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "webhooks.write"
            ]
          }
        ]
      }
    },
    "/v1/webhook-endpoints/{endpointId}/deliveries": {
      "get": {
        "tags": [
          "WebhookEndpoints"
        ],
        "summary": "List failed and retrying deliveries",
        "description": "Lists the deliveries to the endpoint that have not succeeded, for finding and replaying failures.\n\n- **What is listed.** `pending` deliveries (an attempt failed; the next one is at `nextAttemptAt`), soonest first, then `failed` ones (retries ended; kept for 90 days), most recent first. `status` and `eventType` (a webhook type) narrow the list. Each shows its attempts, the receiver's last status and, when failed, `failureReason`; never the message body. `deliveryId` is the `webhook-id` the receiver saw, and `sourceOrganizationId` the organization the event belongs to.\n- **What is never listed.** Successful deliveries, and `spillard.telemetry.live.v1`: a failing live batch is retried for 60 seconds and then dropped.\n- **Sub-organizations.** A pending delivery of a sub-organization's event leaves the log unsent when that organization is no longer below yours.\n- **A live view.** While deliveries are retried, replayed or fail, an entry can move between pages, so a walk through the pages can skip or repeat one.\n\n**Required scope:** `webhooks.read`",
        "operationId": "listWebhookDeliveries",
        "parameters": [
          {
            "name": "endpointId",
            "in": "path",
            "description": "The webhook endpoint's id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "eventType",
            "in": "query",
            "description": "Only deliveries of this webhook type.",
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only `pending` or only `failed` deliveries.",
            "schema": {
              "$ref": "#/components/schemas/WebhookDeliveryStatus"
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Count"
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "The organization whose webhooks to manage: your client's organization when omitted, or another organization your client was granted with every fleet (an entry of `access` in `GET /v1/context` with `allFleets: true`; not an organization below it). An organization your client may read for some fleets only is 403 `auth.fleet_restricted`: its webhooks serve every fleet. Any other organization is 403 `auth.tenant_forbidden`. Endpoints belong to their organization; send the same `organizationId` on every call about an endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryPage"
                },
                "example": {
                  "items": [
                    {
                      "deliveryId": "7c3a9e52-d41b-5f86-8a07-3e9d1b6c4f20",
                      "endpointId": "4b9e2d71-8c5a-4f3e-9d16-a7c0e5b2f849",
                      "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                      "sourceOrganizationId": "6e0b4c93-1a7d-4f52-8b3e-d9a2c5f71e04",
                      "eventType": "spillard.event.raised.v1",
                      "status": "pending",
                      "attemptCount": 2,
                      "lastAttemptAt": "2026-09-29T08:04:10.000Z",
                      "lastResponseStatusCode": 500,
                      "nextAttemptAt": "2026-09-29T08:19:10.000Z",
                      "failedAt": null,
                      "failureReason": null,
                      "replayable": false
                    },
                    {
                      "deliveryId": "8e2d4a71-c63f-5b09-9d4e-1a7f6c2b8e35",
                      "endpointId": "4b9e2d71-8c5a-4f3e-9d16-a7c0e5b2f849",
                      "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                      "sourceOrganizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                      "eventType": "spillard.event.raised.v1",
                      "status": "failed",
                      "attemptCount": null,
                      "lastAttemptAt": "2026-09-29T07:41:16.000Z",
                      "lastResponseStatusCode": 404,
                      "nextAttemptAt": null,
                      "failedAt": "2026-09-29T07:41:16.000Z",
                      "failureReason": "permanent_failure",
                      "replayable": true
                    }
                  ],
                  "hasMore": false,
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.cursor_invalid`: The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.cursor_invalid": {
                    "$ref": "#/components/examples/validation.cursor_invalid"
                  },
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `webhook_endpoint.not_found`: No webhook endpoint with this id belongs to the organization. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "webhook_endpoint.not_found": {
                    "$ref": "#/components/examples/webhook_endpoint.not_found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "webhooks.read"
            ]
          }
        ]
      }
    },
    "/v1/webhook-endpoints/{endpointId}/deliveries/{deliveryId}:replay": {
      "post": {
        "tags": [
          "WebhookEndpoints"
        ],
        "summary": "Replay a failed delivery",
        "description": "Queues a failed delivery again, to recover from a receiver outage or bug without touching the source data. The answer is 202.\n\n- The message keeps its `webhook-id` (deduplicate on it as usual), is signed again with the current secrets and is sent within about a minute. Until it succeeds it is listed as `pending` and retried with a new retry window, so it can arrive after the type's usual window. A delivery that is already pending returns `requeued: false`.\n- 409 `webhook_delivery.not_replayable` when the endpoint does not receive the type now (delivery off, endpoint inactive, type not subscribed or the event no longer matching `eventTypeFilters`), when the message was not kept in full (`replayable` is false: a failed `spillard.telemetry.history.v1` delivery), or while a delivery with the same `webhook-id` is pending for another endpoint of the organization.\n- For a sub-organization's event (`sourceOrganizationId` other than the endpoint's organization), also 409 when the endpoint no longer includes sub-organizations or that organization is no longer below the endpoint's organization; 503 `dependency.unavailable` while the organization structure cannot be read (try again later).\n\n**Required scope:** `webhooks.write`",
        "operationId": "replayWebhookDelivery",
        "parameters": [
          {
            "name": "endpointId",
            "in": "path",
            "description": "The webhook endpoint's id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "deliveryId",
            "in": "path",
            "description": "The delivery's id: the `webhook-id` your endpoint received. An opaque id: treat it as text of up to 255 characters; do not parse it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "organizationId",
            "in": "query",
            "description": "The organization whose webhooks to manage: your client's organization when omitted, or another organization your client was granted with every fleet (an entry of `access` in `GET /v1/context` with `allFleets: true`; not an organization below it). An organization your client may read for some fleets only is 403 `auth.fleet_restricted`: its webhooks serve every fleet. Any other organization is 403 `auth.tenant_forbidden`. Endpoints belong to their organization; send the same `organizationId` on every call about an endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryReplayAccepted"
                },
                "example": {
                  "deliveryId": "8e2d4a71-c63f-5b09-9d4e-1a7f6c2b8e35",
                  "requeued": true
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found. `errorCode`:\n\n- `webhook_delivery.not_found`: No pending or failed delivery with this id belongs to the endpoint; delivered messages are not listed. Check the id in the delivery list.\n- `webhook_endpoint.not_found`: No webhook endpoint with this id belongs to the organization. Check the id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "webhook_delivery.not_found": {
                    "$ref": "#/components/examples/webhook_delivery.not_found"
                  },
                  "webhook_endpoint.not_found": {
                    "$ref": "#/components/examples/webhook_endpoint.not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `errorCode`:\n\n- `webhook_delivery.not_replayable`: The delivery cannot be replayed now: the endpoint does not receive its type any more, the message was not kept in full, or a delivery of the same message is pending. Check the endpoint settings, or wait for the pending delivery.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "webhook_delivery.not_replayable": {
                    "$ref": "#/components/examples/webhook_delivery.not_replayable"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "webhooks.write"
            ]
          }
        ]
      }
    },
    "/v1/webhook-event-types": {
      "get": {
        "tags": [
          "WebhookEndpoints"
        ],
        "summary": "List webhook types",
        "description": "Lists the six webhook types an endpoint can subscribe to (`eventTypes` of an endpoint), each with its description and a link to its message schema. They are not the device event types, such as `dsm.fatigue`, that `eventTypeFilters` takes. Every message is a CloudEvents 1.0 envelope signed with Standard Webhooks headers (`webhook-id`, `webhook-timestamp`, `webhook-signature`). It reads no fleet data: any valid token works and no scope is needed.",
        "operationId": "listWebhookEventTypes",
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEventTypeCatalog"
                },
                "example": {
                  "eventTypes": [
                    {
                      "type": "spillard.alarm.raised.v1",
                      "description": "A device raised an alarm that Spillard accepted. `kind` says what raised it; `eventId` is the event Spillard created from it, or null. That event is also delivered as a `spillard.event.raised.v1` message to the endpoints subscribed to that type.",
                      "samplePayloadRef": "#/components/schemas/AlarmRaisedMessage"
                    },
                    {
                      "type": "spillard.event.raised.v1",
                      "description": "A device raised an event. `eventTypes` holds its device event types as `family.name` tokens (for example `dsm.fatigue`); an endpoint can filter on them with `eventTypeFilters`. `links.self` is `/v1/events/{eventId}` and `links.media` is `/v1/events/{eventId}/media`.",
                      "samplePayloadRef": "#/components/schemas/EventRaisedMessage"
                    },
                    {
                      "type": "spillard.journey.completed.v1",
                      "description": "A journey was completed or reprocessed. The latest `revision` wins; delete the ids in `replaces`. No `driver` in v1: Spillard does not know who drove a journey.",
                      "samplePayloadRef": "#/components/schemas/JourneyCompletedMessage"
                    },
                    {
                      "type": "spillard.telemetry.live.v1",
                      "description": "The latest positions of the organization's vehicles, batched every 5 seconds.",
                      "samplePayloadRef": "#/components/schemas/TelemetryLiveMessage"
                    },
                    {
                      "type": "spillard.telemetry.history.v1",
                      "description": "Recorded telemetry tracks of the organization's devices, batched, late uploads included. Tracks carry no `driver` in v1.",
                      "samplePayloadRef": "#/components/schemas/TelemetryHistoryMessage"
                    },
                    {
                      "type": "spillard.media.request.completed.v1",
                      "description": "A video request ended with `outcome` completed, failed or no_data. Only `completed` is reliable today; `failed` and `no_data` are in future development, do not rely on them. Poll `GET /v1/media-requests/{mediaRequestId}` until `state` is final: a request that ends without an answer from the device may send no message. `links.media` points to `/v1/media-requests/{mediaRequestId}/media`.",
                      "samplePayloadRef": "#/components/schemas/MediaRequestCompletedMessage"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token's access list is damaged (`auth.tenant_forbidden`); ask Spillard to check the API client.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [ ]
          }
        ]
      }
    },
    "/v1/webhooks/message-schemas": {
      "get": {
        "tags": [
          "WebhookEndpoints"
        ],
        "summary": "Get example webhook messages",
        "description": "Returns one example message per webhook type, each a CloudEvents 1.0 envelope; its schemas describe every field of the messages. It gives generated clients a model for each message; the Webhooks section of the reference describes the same messages.",
        "operationId": "getWebhookMessageExamples",
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookMessageExamples"
                },
                "example": {
                  "alarmRaised": {
                    "specversion": "1.0",
                    "id": "0b7c3e9a-51d4-5f28-a6c1-9e3d7f0b2a64",
                    "type": "spillard.alarm.raised.v1",
                    "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                    "subject": "alarms/4d2f8a61-9c7e-4b15-83d0-a6e5f1c2b978",
                    "time": "2026-09-29T07:12:55.000Z",
                    "datacontenttype": "application/json",
                    "data": {
                      "alarmId": "4d2f8a61-9c7e-4b15-83d0-a6e5f1c2b978",
                      "triggeredAt": "2026-09-29T07:12:55.000Z",
                      "kind": "io_input",
                      "input": 2,
                      "vendorKey": "ME41_ALARM_IO_ALARM2",
                      "location": {
                        "latitude": 51.53184,
                        "longitude": -0.262915
                      },
                      "speedKph": 0,
                      "headingDegrees": 90,
                      "eventId": null,
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                        "registration": "SL24 WHK"
                      },
                      "driver": {
                        "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                      },
                      "device": {
                        "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                        "hardwareId": "009a2c41f7"
                      }
                    }
                  },
                  "eventRaised": {
                    "specversion": "1.0",
                    "id": "8e2d4a71-c63f-5b09-9d4e-1a7f6c2b8e35",
                    "type": "spillard.event.raised.v1",
                    "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                    "subject": "events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                    "time": "2026-09-29T07:41:17.000Z",
                    "datacontenttype": "application/json",
                    "data": {
                      "eventId": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                      "triggeredAt": "2026-09-29T07:41:17.000Z",
                      "receivedAt": "2026-09-29T07:41:19.412Z",
                      "eventTypes": [
                        "driver_behaviour.harsh_braking"
                      ],
                      "classification": "medium",
                      "origin": "device",
                      "location": {
                        "latitude": 51.530112,
                        "longitude": -0.292731
                      },
                      "address": {
                        "label": "Western Avenue (A40), Ealing, London, United Kingdom",
                        "street": "Western Avenue",
                        "city": "London",
                        "postalCode": null,
                        "countryCode": "GBR"
                      },
                      "speedKph": 54,
                      "maxSpeedKph": 57,
                      "speedLimitKph": 64,
                      "headingDegrees": 263,
                      "mediaExpected": true,
                      "firmwareVersion": "T250922.01",
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                        "registration": "SL24 WHK"
                      },
                      "driver": {
                        "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                      },
                      "device": {
                        "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                        "hardwareId": "009a2c41f7"
                      },
                      "links": {
                        "self": "/v1/events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                        "media": "/v1/events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53/media"
                      }
                    }
                  },
                  "journeyCompleted": {
                    "specversion": "1.0",
                    "id": "5a9f1c3e-7d2b-5e84-b6a0-c3e8d1f7a492",
                    "type": "spillard.journey.completed.v1",
                    "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                    "subject": "journeys/20260929T065804000Z009a2c41f7",
                    "time": "2026-09-29T08:02:40.000Z",
                    "datacontenttype": "application/json",
                    "data": {
                      "journeyId": "20260929T065804000Z009a2c41f7",
                      "revision": 1,
                      "replaces": [ ],
                      "startedAt": "2026-09-29T06:58:04.000Z",
                      "endedAt": "2026-09-29T08:02:40.000Z",
                      "start": {
                        "location": {
                          "latitude": 51.532704,
                          "longitude": -0.268213
                        },
                        "address": {
                          "label": "Coronation Road, Park Royal, London, United Kingdom",
                          "street": "Coronation Road",
                          "city": "London",
                          "postalCode": null,
                          "countryCode": "GBR"
                        }
                      },
                      "end": {
                        "location": {
                          "latitude": 51.481022,
                          "longitude": -0.429587
                        },
                        "address": {
                          "label": "Bath Road, Harlington, Hayes, United Kingdom",
                          "street": "Bath Road",
                          "city": "Hayes",
                          "postalCode": null,
                          "countryCode": "GBR"
                        }
                      },
                      "distanceMeters": 21480,
                      "durationSeconds": 3876,
                      "movingSeconds": 3102,
                      "idleSeconds": 774,
                      "ignitionOnSeconds": 3876,
                      "maxSpeedKph": 81,
                      "speedingCount": 2,
                      "gpsCoverage": "full",
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                        "registration": "SL24 WHK"
                      },
                      "device": {
                        "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                        "hardwareId": "009a2c41f7"
                      }
                    }
                  },
                  "telemetryLive": {
                    "specversion": "1.0",
                    "id": "d3a7e1f9-4b6c-5d82-a9e3-6f1c0b8d4e27",
                    "type": "spillard.telemetry.live.v1",
                    "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                    "time": "2026-09-29T07:41:18.000Z",
                    "datacontenttype": "application/json",
                    "data": {
                      "positions": [
                        {
                          "recordedAt": "2026-09-29T07:41:18.000Z",
                          "location": {
                            "latitude": 51.530103,
                            "longitude": -0.292884
                          },
                          "speedKph": 38,
                          "headingDegrees": 263,
                          "ignitionOn": true,
                          "organization": {
                            "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                          },
                          "fleet": {
                            "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                          },
                          "vehicle": {
                            "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                            "registration": "SL24 WHK"
                          },
                          "driver": {
                            "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                          },
                          "device": {
                            "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                            "hardwareId": "009a2c41f7"
                          }
                        },
                        {
                          "recordedAt": "2026-09-29T07:41:16.000Z",
                          "location": {
                            "latitude": 51.532655,
                            "longitude": -0.268391
                          },
                          "speedKph": 0,
                          "headingDegrees": 182,
                          "ignitionOn": false,
                          "organization": {
                            "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                          },
                          "fleet": {
                            "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                          },
                          "vehicle": {
                            "id": "5d8c2b7e-1a4f-4e93-8b6d-0f3a9c7e2d51",
                            "registration": "SL24 WHN"
                          },
                          "driver": null,
                          "device": {
                            "id": "2c7e9a14-58b3-4f06-9d21-a4e8f3b6c051",
                            "hardwareId": "009a2c4388"
                          }
                        }
                      ]
                    }
                  },
                  "telemetryHistory": {
                    "specversion": "1.0",
                    "id": "71c4e8b2-3d9f-5a16-b8e7-4a2d6c9f0e53",
                    "type": "spillard.telemetry.history.v1",
                    "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                    "time": "2026-09-29T07:41:19.000Z",
                    "datacontenttype": "application/json",
                    "data": {
                      "tracks": [
                        {
                          "windowStartAt": "2026-09-29T07:41:15.000Z",
                          "windowEndAt": "2026-09-29T07:41:19.000Z",
                          "backfill": false,
                          "frameCount": 5,
                          "frames": [
                            {
                              "recordedAt": "2026-09-29T07:41:15.000Z",
                              "location": {
                                "latitude": 51.53014,
                                "longitude": -0.292299
                              },
                              "speedKph": 57,
                              "headingDegrees": 263,
                              "gpsValid": true,
                              "ignitionOn": true
                            },
                            {
                              "recordedAt": "2026-09-29T07:41:16.000Z",
                              "location": {
                                "latitude": 51.530126,
                                "longitude": -0.292526
                              },
                              "speedKph": 56,
                              "headingDegrees": 263,
                              "gpsValid": true,
                              "ignitionOn": true
                            },
                            {
                              "recordedAt": "2026-09-29T07:41:17.000Z",
                              "location": {
                                "latitude": 51.530112,
                                "longitude": -0.292731
                              },
                              "speedKph": 54,
                              "headingDegrees": 263,
                              "gpsValid": true,
                              "ignitionOn": true
                            },
                            {
                              "recordedAt": "2026-09-29T07:41:18.000Z",
                              "location": {
                                "latitude": 51.530103,
                                "longitude": -0.292884
                              },
                              "speedKph": 38,
                              "headingDegrees": 263,
                              "gpsValid": true,
                              "ignitionOn": true
                            },
                            {
                              "recordedAt": "2026-09-29T07:41:19.000Z",
                              "location": {
                                "latitude": 51.530097,
                                "longitude": -0.29298
                              },
                              "speedKph": 24,
                              "headingDegrees": 263,
                              "gpsValid": true,
                              "ignitionOn": true
                            }
                          ],
                          "organization": {
                            "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                          },
                          "fleet": {
                            "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                          },
                          "vehicle": {
                            "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                            "registration": "SL24 WHK"
                          },
                          "device": {
                            "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                            "hardwareId": "009a2c41f7"
                          }
                        },
                        {
                          "windowStartAt": "2026-09-29T05:12:00.000Z",
                          "windowEndAt": "2026-09-29T05:12:02.000Z",
                          "backfill": true,
                          "frameCount": 3,
                          "frames": [
                            {
                              "recordedAt": "2026-09-29T05:12:00.000Z",
                              "location": {
                                "latitude": 51.509812,
                                "longitude": -0.378655
                              },
                              "speedKph": 0,
                              "headingDegrees": 104,
                              "gpsValid": true,
                              "ignitionOn": false
                            },
                            {
                              "recordedAt": "2026-09-29T05:12:01.000Z",
                              "location": {
                                "latitude": 51.509812,
                                "longitude": -0.378655
                              },
                              "speedKph": 0,
                              "headingDegrees": 104,
                              "gpsValid": true,
                              "ignitionOn": true
                            },
                            {
                              "recordedAt": "2026-09-29T05:12:02.000Z",
                              "location": {
                                "latitude": 51.509808,
                                "longitude": -0.378631
                              },
                              "speedKph": 6,
                              "headingDegrees": 104,
                              "gpsValid": true,
                              "ignitionOn": true
                            }
                          ],
                          "organization": {
                            "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                          },
                          "fleet": {
                            "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                          },
                          "vehicle": {
                            "id": "e2b6f9c4-7d31-4a8e-9c52-8f1d0a6b3e97",
                            "registration": "SL73 KPE"
                          },
                          "device": {
                            "id": "b84e2c6a-91f3-4d70-a5c8-3e6f0d9b2741",
                            "hardwareId": "009a2c45d2"
                          }
                        }
                      ]
                    }
                  },
                  "mediaRequestCompleted": {
                    "specversion": "1.0",
                    "id": "3c1f9e7d-8a4b-5f26-b0d9-e2c6a4f8b175",
                    "type": "spillard.media.request.completed.v1",
                    "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                    "subject": "mediaRequests/9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683",
                    "time": "2026-09-29T08:07:48.000Z",
                    "datacontenttype": "application/json",
                    "data": {
                      "mediaRequestId": "9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683",
                      "trigger": "manual",
                      "externalId": "INS-CLM-2026-004417",
                      "outcome": "completed",
                      "requestedAt": "2026-09-29T08:05:11.000Z",
                      "completedAt": "2026-09-29T08:07:48.000Z",
                      "request": {
                        "startAt": "2026-09-29T07:41:02.000Z",
                        "durationSeconds": 30,
                        "channels": [
                          1,
                          2
                        ],
                        "overlay": true
                      },
                      "eventId": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                        "registration": "SL24 WHK"
                      },
                      "driver": {
                        "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                      },
                      "device": {
                        "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                        "hardwareId": "009a2c41f7"
                      },
                      "links": {
                        "media": "/v1/media-requests/9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683/media",
                        "event": "/v1/events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token's access list is damaged (`auth.tenant_forbidden`); ask Spillard to check the API client.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [ ]
          }
        ],
        "x-scalar-ignore": true
      }
    },
    "/v1/webhook-settings": {
      "get": {
        "tags": [
          "WebhookSettings"
        ],
        "summary": "Get the webhook delivery switch",
        "description": "Returns whether webhook delivery is on for the organization (`enabled`) and how many endpoints it can have (`endpointLimit`).\n\n- While `enabled` is false, nothing is delivered to any endpoint, even an active one; the endpoints and their subscriptions are kept. An organization whose delivery was never turned on reads as false.\n- The switch also covers the sub-organizations' events delivered to the organization's endpoints; a sub-organization's own switch does not affect them.\n\n**Required scope:** `webhook-settings.read`",
        "operationId": "getWebhookSettings",
        "parameters": [
          {
            "name": "organizationId",
            "in": "query",
            "description": "The organization whose webhooks to manage: your client's organization when omitted, or another organization your client was granted with every fleet (an entry of `access` in `GET /v1/context` with `allFleets: true`; not an organization below it). An organization your client may read for some fleets only is 403 `auth.fleet_restricted`: its webhooks serve every fleet. Any other organization is 403 `auth.tenant_forbidden`. Endpoints belong to their organization; send the same `organizationId` on every call about an endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSettings"
                },
                "example": {
                  "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                  "enabled": true,
                  "endpointLimit": 10
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "webhook-settings.read"
            ]
          }
        ]
      },
      "put": {
        "tags": [
          "WebhookSettings"
        ],
        "summary": "Turn webhook delivery on or off",
        "description": "Sets the organization's webhook delivery switch and returns it. A change takes effect within about a minute; `enabled` is required (400 `validation.failed` without it).\n\n- `{\"enabled\": true}` starts delivering to the active endpoints: do it after creating an endpoint and checking it with `POST /v1/webhook-endpoints/{endpointId}:test`.\n- `{\"enabled\": false}` stops every delivery: new messages are neither delivered nor kept for later, and pending retries end as `failed` with `failureReason` `disabled`.\n- The switch also covers the sub-organizations' events delivered to the organization's endpoints; a sub-organization's own switch does not affect them, and this one does not affect the sub-organizations' own endpoints.\n- Creating, changing or deleting an endpoint never changes the switch.\n\n**Required scope:** `webhook-settings.write`",
        "operationId": "updateWebhookSettings",
        "parameters": [
          {
            "name": "organizationId",
            "in": "query",
            "description": "The organization whose webhooks to manage: your client's organization when omitted, or another organization your client was granted with every fleet (an entry of `access` in `GET /v1/context` with `allFleets: true`; not an organization below it). An organization your client may read for some fleets only is 403 `auth.fleet_restricted`: its webhooks serve every fleet. Any other organization is 403 `auth.tenant_forbidden`. Endpoints belong to their organization; send the same `organizationId` on every call about an endpoint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookSettingsRequest"
              },
              "example": {
                "enabled": true
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSettings"
                },
                "example": {
                  "organizationId": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                  "enabled": true,
                  "endpointLimit": 10
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. The bearer token is missing, malformed or expired, or was issued for another API (`auth.token_invalid`). Get a new token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.token_invalid": {
                    "$ref": "#/components/examples/auth.token_invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden. The token lacks the scope the operation requires (`auth.scope_missing`), or the organization is outside what your client may read (`auth.tenant_forbidden`). Also:\n\n- `auth.fleet_restricted`: Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "auth.scope_missing": {
                    "$ref": "#/components/examples/auth.scope_missing"
                  },
                  "auth.tenant_forbidden": {
                    "$ref": "#/components/examples/auth.tenant_forbidden"
                  },
                  "auth.fleet_restricted": {
                    "$ref": "#/components/examples/auth.fleet_restricted"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests. The API client sent more requests than its rate limit allows (`rate.limit_exceeded`); wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before the next request.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate.limit_exceeded": {
                    "$ref": "#/components/examples/rate.limit_exceeded"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error. An unexpected error (`server.unexpected`); retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "server.unexpected": {
                    "$ref": "#/components/examples/server.unexpected"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request. `errorCode`:\n\n- `validation.failed`: A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "validation.failed": {
                    "$ref": "#/components/examples/validation.failed"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable. `errorCode`:\n\n- `dependency.unavailable`: A Spillard service is temporarily unavailable. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.unavailable": {
                    "$ref": "#/components/examples/dependency.unavailable"
                  }
                }
              }
            }
          },
          "504": {
            "description": "Gateway Timeout. `errorCode`:\n\n- `dependency.timeout`: A Spillard service did not answer in time. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "dependency.timeout": {
                    "$ref": "#/components/examples/dependency.timeout"
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Media Type. `errorCode`:\n\n- `request.unsupported_media_type`: The body is not JSON. Send it with `Content-Type: application/json`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "request.unsupported_media_type": {
                    "$ref": "#/components/examples/request.unsupported_media_type"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "webhook-settings.write"
            ]
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "AccessGrant": {
        "required": [
          "organization",
          "includeSubOrganizations",
          "allFleets",
          "fleets"
        ],
        "type": "object",
        "properties": {
          "organization": {
            "description": "The granted organization.",
            "$ref": "#/components/schemas/IdReference"
          },
          "includeSubOrganizations": {
            "type": "boolean",
            "description": "True when the access also covers every sub-organization of this organization, at any depth."
          },
          "allFleets": {
            "type": "boolean",
            "description": "True when the access covers every fleet of the organization (and of its sub-organizations when they are included). False when it is limited to `fleets`."
          },
          "fleets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IdReference"
            },
            "description": "The fleets the access is limited to. Empty when `allFleets` is true. An access limited to fleets that no longer exist lists none and returns no data."
          }
        },
        "description": "One organization of an API client's access: the organization, whether the access also covers its sub-organizations,\nand whether it covers every fleet or only the listed ones. For an organization granted for some fleets only, the\nAPI returns data of those fleets alone. Such an organization is still listed in `/v1/organizations`."
      },
      "Address": {
        "type": "object",
        "properties": {
          "label": {
            "type": [
              "null",
              "string"
            ],
            "description": "The whole address on one line. Null when not available."
          },
          "country": {
            "type": [
              "null",
              "string"
            ],
            "description": "The country name. Null when not available."
          },
          "state": {
            "type": [
              "null",
              "string"
            ],
            "description": "The state, region or province. Null when not available."
          },
          "county": {
            "type": [
              "null",
              "string"
            ],
            "description": "The county. Null when not available."
          },
          "city": {
            "type": [
              "null",
              "string"
            ],
            "description": "The city or town. Null when not available."
          },
          "district": {
            "type": [
              "null",
              "string"
            ],
            "description": "The district or borough. Null when not available."
          },
          "street": {
            "type": [
              "null",
              "string"
            ],
            "description": "The street name. Null when not available."
          },
          "houseNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The house or building number. Null when not available."
          },
          "postalCode": {
            "type": [
              "null",
              "string"
            ],
            "description": "The postal or ZIP code. Null when not available."
          }
        },
        "description": "A street address. Any part is null when unknown. When only a formatted address is known, `label` carries it."
      },
      "AlarmKind": {
        "enum": [
          "io_input",
          "emergency",
          "dsm",
          "adas",
          "acceleration",
          "user_defined"
        ],
        "type": "string",
        "description": "What raised an alarm.",
        "x-enumDescriptions": {
          "io_input": "An input of the device; `input` says which.",
          "emergency": "The emergency (panic) button.",
          "dsm": "Driver state monitoring: fatigue, distraction, phone use and the like.",
          "adas": "Driver assistance: collision or lane warnings, human detection and the like.",
          "acceleration": "An acceleration alarm of the device's motion sensor.",
          "user_defined": "An alarm defined on the device."
        }
      },
      "AlarmRaisedData": {
        "required": [
          "alarmId",
          "triggeredAt",
          "kind",
          "organization",
          "device"
        ],
        "type": "object",
        "properties": {
          "alarmId": {
            "type": "string",
            "description": "The alarm's id. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "triggeredAt": {
            "type": "string",
            "description": "When the alarm triggered (UTC, ISO 8601).",
            "format": "date-time"
          },
          "kind": {
            "type": "string",
            "description": "What raised the alarm. Known values: `io_input`, `emergency`, `dsm`, `adas`, `acceleration`, `user_defined`. More values can be added; handle one you do not know."
          },
          "input": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The number of the device input that raised the alarm; null unless `kind` is `io_input`.",
            "format": "int32"
          },
          "vendorKey": {
            "type": [
              "null",
              "string"
            ],
            "description": "The alarm's name as the device reports it (the Spillard web app's Alarm key), for example `ME41_ALARM_IO_ALARM2`; null when unknown."
          },
          "location": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/GeoPoint"
              }
            ],
            "description": "Where the device was when the alarm triggered; null without a position."
          },
          "speedKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "Speed in km/h when the alarm triggered; null when unknown.",
            "format": "int32"
          },
          "headingDegrees": {
            "type": [
              "null",
              "integer"
            ],
            "description": "Heading in degrees (0 to 359, clockwise from north); null when unknown.",
            "format": "int32"
          },
          "eventId": {
            "type": [
              "null",
              "string"
            ],
            "description": "The event created from the alarm: `GET /v1/events/{eventId}` reads it. Null when the alarm created no event. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "organization": {
            "description": "The organization the alarm belongs to (also when the message reaches an endpoint of an organization above it).",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet of the device's vehicle, or null when it has none."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/WebhookVehicleReference"
              }
            ],
            "description": "The vehicle the device is fitted to, or null when it is not fitted to one."
          },
          "driver": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle's driver, or null when none is known."
          },
          "device": {
            "description": "The device that raised the alarm.",
            "$ref": "#/components/schemas/DeviceReference"
          }
        },
        "description": "The `data` of `spillard.alarm.raised.v1`: an alarm a device raised (an input, the emergency button, a\ndriver monitoring or driver assistance alarm). The envelope's `subject` is `alarms/{alarmId}` and its\n`time` is `triggeredAt`."
      },
      "AlarmRaisedMessage": {
        "required": [
          "id",
          "type",
          "source",
          "time",
          "data",
          "specversion",
          "datacontenttype"
        ],
        "type": "object",
        "properties": {
          "specversion": {
            "const": "1.0",
            "type": "string",
            "description": "The CloudEvents specification version, always `1.0`."
          },
          "id": {
            "type": "string",
            "description": "The message id, equal to the `webhook-id` header. A retry keeps the id: deduplicate on it. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "type": {
            "const": "spillard.alarm.raised.v1",
            "type": "string",
            "description": "The webhook type, always `spillard.alarm.raised.v1` for this message."
          },
          "source": {
            "type": "string",
            "description": "The organization the fact belongs to: `/organizations/{organizationId}`."
          },
          "subject": {
            "type": [
              "null",
              "string"
            ],
            "description": "The entity the fact is about (for example `events/{eventId}`); absent from the `telemetry.live` and `telemetry.history` batches."
          },
          "time": {
            "type": "string",
            "description": "When the fact occurred (UTC, milliseconds).",
            "format": "date-time"
          },
          "datacontenttype": {
            "const": "application/json",
            "type": "string",
            "description": "The media type of `data`, always `application/json`."
          },
          "data": {
            "description": "The payload of the message.",
            "$ref": "#/components/schemas/AlarmRaisedData"
          }
        },
        "description": "The `spillard.alarm.raised.v1` webhook message: a CloudEvents 1.0 envelope in structured mode whose `data` carries the payload. Within v1 the envelope and payload only gain fields: tolerate unknown fields."
      },
      "ApiClient": {
        "required": [
          "clientId",
          "name",
          "organizationId",
          "scopes",
          "active",
          "createdAt",
          "access"
        ],
        "type": "object",
        "properties": {
          "clientId": {
            "type": "string",
            "description": "The public client id you send to the token endpoint. It never changes."
          },
          "name": {
            "type": "string",
            "description": "The client's name, 1 to 200 characters."
          },
          "organizationId": {
            "type": "string",
            "description": "The organization the client belongs to (its primary organization). What the client may read is in `access`.",
            "format": "uuid"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The scopes granted to the client. They are a subset of the scopes of the client that created it. Examples: `organizations.read`, `fleets.read`, `fleets.write`. See the `Scope` model for the values known today. More values can be added; handle one you do not know."
          },
          "secretHint": {
            "type": [
              "null",
              "string"
            ],
            "description": "The last 4 characters of the current client secret. Null while the client has no secret."
          },
          "active": {
            "type": "boolean",
            "description": "True when the client may obtain tokens."
          },
          "createdAt": {
            "type": "string",
            "description": "When the client was created (UTC, ISO 8601).",
            "format": "date-time"
          },
          "access": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccessGrant"
            },
            "description": "What the client may read (read-only): its organizations, each with or without its sub-organizations, and with every fleet or only listed fleets. A client created through this API reads its own organization only. Spillard sets up clients with several organizations or with fleet limits."
          },
          "previousSecretExpiresAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the secret replaced by the latest rotation stops working (UTC, ISO 8601): 24 hours after the rotation. Null when no replaced secret is still valid.",
            "format": "date-time"
          }
        },
        "description": "An API client: an OAuth2 client for server-to-server access. Its secret is never returned; `secretHint` shows\nits last 4 characters."
      },
      "ApiClientCreated": {
        "required": [
          "clientId",
          "clientSecret",
          "secretHint",
          "name",
          "organizationId",
          "scopes"
        ],
        "type": "object",
        "properties": {
          "clientId": {
            "type": "string",
            "description": "The public client id you send to the token endpoint. It never changes."
          },
          "clientSecret": {
            "type": "string",
            "description": "The client secret. It is shown only in this response."
          },
          "secretHint": {
            "type": "string",
            "description": "The last 4 characters of the client secret."
          },
          "name": {
            "type": "string",
            "description": "The client's name."
          },
          "organizationId": {
            "type": "string",
            "description": "The organization the client was created in.",
            "format": "uuid"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The scopes granted to the client. Examples: `organizations.read`, `fleets.read`, `fleets.write`. See the `Scope` model for the values known today. More values can be added; handle one you do not know."
          }
        },
        "description": "A new API client with its secret. The secret is shown only once, so store it safely."
      },
      "ApiClientPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiClient"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of API clients."
      },
      "ApiClientSecretRotated": {
        "required": [
          "clientSecret",
          "secretHint"
        ],
        "type": "object",
        "properties": {
          "clientSecret": {
            "type": "string",
            "description": "The new client secret. It is shown only in this response."
          },
          "secretHint": {
            "type": "string",
            "description": "The last 4 characters of the new client secret."
          },
          "previousSecretExpiresAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the replaced secret stops working (UTC, ISO 8601): 24 hours after the rotation. Null when the client had no valid secret to replace.",
            "format": "date-time"
          }
        },
        "description": "A new client secret. It is shown only once, so store it safely."
      },
      "ApiStatus": {
        "required": [
          "status",
          "version",
          "environment",
          "serverTime"
        ],
        "type": "object",
        "properties": {
          "status": {
            "const": "ok",
            "type": "string",
            "description": "Always `ok` when the API answers."
          },
          "version": {
            "type": "string",
            "description": "The version of the API build that served the request."
          },
          "environment": {
            "type": "string",
            "description": "The name of the hosting environment, for example `Production` or `Development`."
          },
          "serverTime": {
            "type": "string",
            "description": "The server clock when the response was produced (UTC, ISO 8601).",
            "format": "date-time"
          }
        },
        "description": "The service status: the API build, the environment and the server time. It contains no fleet data."
      },
      "AuthContext": {
        "required": [
          "clientId",
          "organizationId",
          "licenses",
          "access",
          "scopes"
        ],
        "type": "object",
        "properties": {
          "clientId": {
            "type": "string",
            "description": "The id of the authenticated API client."
          },
          "organizationId": {
            "type": "string",
            "description": "The client's primary organization. It owns the client, its licenses and, by default, its webhooks. What the client may read is in `access`.",
            "format": "uuid"
          },
          "organizationName": {
            "type": [
              "null",
              "string"
            ],
            "description": "The primary organization's display name. Null when it has none."
          },
          "licenses": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The licenses of the client's organization, as feature keys. Empty when there are none."
          },
          "access": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccessGrant"
            },
            "description": "What your client may read: one entry per organization it was granted, the primary organization first. Each entry says whether its sub-organizations are included and whether it covers every fleet or only the listed fleets. It shows the access in effect now: an organization or fleet that no longer exists is left out."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The OAuth scopes granted to the token. Empty when there are none."
          },
          "dataAccessDays": {
            "type": [
              "null",
              "integer"
            ],
            "description": "How many days back the primary organization's data can be read: 30 when its contract limits it to the last 30 days, null when there is no limit. Older data is hidden. Lists leave out events triggered and journeys started before the limit. Reading one of them by id (also a journey's frames and events) is 403 `data.retention_restricted`. The media of an older event is an empty list. A media request or a share for older footage is 403. The human detection metric has no row for an older day. Another organization can have its own limit, so its older data can be hidden even when this value is null.",
            "format": "int32"
          }
        },
        "description": "How the API sees your token: your client, its organization, the access it has now, its licenses and its scopes."
      },
      "BatchMoveVehicleItem": {
        "required": [
          "vehicleId",
          "fleetId"
        ],
        "type": "object",
        "properties": {
          "vehicleId": {
            "type": "string",
            "description": "The id of the vehicle to move (UUID).",
            "format": "uuid"
          },
          "fleetId": {
            "type": "string",
            "description": "The id of the target fleet. The rules of `POST /v1/vehicles/{vehicleId}:move` apply.",
            "format": "uuid"
          }
        },
        "description": "One move of a batch: a vehicle and the fleet it moves to."
      },
      "BatchMoveVehicleResult": {
        "required": [
          "vehicle",
          "fleet",
          "moved"
        ],
        "type": "object",
        "properties": {
          "vehicle": {
            "description": "The vehicle of the move.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "description": "The target fleet of the move.",
            "$ref": "#/components/schemas/IdReference"
          },
          "moved": {
            "type": "boolean",
            "description": "True when the vehicle was moved. Then every vehicle of the batch was moved."
          },
          "errorCode": {
            "type": [
              "null",
              "string"
            ],
            "description": "Why the move was refused. Null when the vehicle was moved. One of `vehicle.not_found` (the vehicle, its driver or one of its devices is outside your access or does not exist), `validation.reference_not_found` (the target fleet), `vehicle.already_in_fleet`, `resource.link_conflict` (the target organization has a vehicle with the same registration number, or another move of the batch takes the same driver elsewhere), or `batch.aborted` (this move was valid, but another move of the batch was refused)."
          },
          "detail": {
            "type": [
              "null",
              "string"
            ],
            "description": "A readable explanation of `errorCode`. Null when the vehicle was moved."
          }
        },
        "description": "The outcome of one move of a batch."
      },
      "BatchMoveVehiclesRequest": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "maxItems": 100,
            "minItems": 1,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BatchMoveVehicleItem"
            },
            "description": "The moves. All are applied together, or none is."
          }
        },
        "description": "A batch of vehicle moves: 1 to 100 moves, each vehicle at most once."
      },
      "BatchMoveVehiclesResult": {
        "required": [
          "moved",
          "processedCount",
          "failedCount",
          "results"
        ],
        "type": "object",
        "properties": {
          "moved": {
            "type": "boolean",
            "description": "True when every vehicle was moved. False when none was."
          },
          "processedCount": {
            "type": "integer",
            "description": "The number of vehicles moved: every item when `moved` is true, otherwise 0.",
            "format": "int32"
          },
          "failedCount": {
            "type": "integer",
            "description": "The number of moves refused for a reason of their own, that is, with any `errorCode` but `batch.aborted`. 0 when `moved` is true.",
            "format": "int32"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BatchMoveVehicleResult"
            },
            "description": "One result per item, in the order of the request."
          }
        },
        "description": "The result of a batch of vehicle moves. Every move is checked first. When one is refused, none is made and the other\nmoves are `batch.aborted`. Otherwise all are made together."
      },
      "Channel": {
        "required": [
          "number"
        ],
        "type": "object",
        "properties": {
          "number": {
            "maximum": 32,
            "minimum": 1,
            "type": "integer",
            "description": "The channel number, from 1 to 32. Use it in the `channels` of a video request.",
            "format": "int32"
          },
          "name": {
            "type": [
              "null",
              "string"
            ],
            "description": "The channel's label as set in the Spillard web app for the device, its organization or its device model (for example \"Forward facing\" or \"Interior\"). Null when the channel has no label."
          }
        },
        "description": "A camera channel of a device."
      },
      "Command": {
        "required": [
          "id",
          "state",
          "type",
          "organization",
          "device",
          "createdAt",
          "manual"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The command's id. `GET /v1/commands/{commandId}` reads it. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "state": {
            "type": "string",
            "description": "The command's state. The Spillard web app shows `dispatched` as Sent, `acknowledged` as Received, `completed` as Done and `timed_out` as Inconclusive. Known values: `queued`, `dispatched`, `acknowledged`, `completed`, `cancelled`, `failed`, `timed_out`. In progress: `queued`, `dispatched`, `acknowledged`. Final: `completed`, `cancelled`, `failed`, `timed_out`. The final states do not change within v1, so poll until `state` is one of them. More in-progress states can be added; treat a state you do not know as in progress."
          },
          "type": {
            "type": "string",
            "description": "The command's type, for example `video` or `reboot`. It is the type of `payload`. Known values: `video`, `format`, `reboot`, `firmware_update`, `configuration_update`, `download_logs`, `download_configuration`, `custom_command`, `event_acceleration_data`, `unknown`. More values can be added; handle one you do not know."
          },
          "organization": {
            "description": "The organization that owns the command.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet the command targets. Null when the command is not tied to a fleet."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle the command targets. Null when the command is not tied to a vehicle."
          },
          "event": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/EventReference"
              }
            ],
            "description": "The event the command is linked to: for a video request, the event it belongs to. Null when it is not linked to an event."
          },
          "device": {
            "description": "The device the command was sent to.",
            "$ref": "#/components/schemas/DeviceReference"
          },
          "createdAt": {
            "type": "string",
            "description": "When the command was created (UTC, ISO 8601).",
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the command last changed (UTC, ISO 8601). Null when it has not changed since it was created.",
            "format": "date-time"
          },
          "payload": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CommandPayload"
              }
            ],
            "description": "The command's parameters. Null for commands without parameters (for example a reboot)."
          },
          "driver": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The driver assigned to the vehicle when the command was created. Null when there was none."
          },
          "createdBy": {
            "type": [
              "null",
              "string"
            ],
            "description": "The user who created the command. Null for a command Spillard created itself."
          },
          "manual": {
            "type": "boolean",
            "description": "True when a user or an API client asked for the command, including media requests made through this API. False when Spillard created it automatically, for example a video request for an event (`trigger``event` in the webhook)."
          },
          "failureReason": {
            "type": [
              "null",
              "string"
            ],
            "description": "The failure text the device gave for a `failed` or `timed_out` command, at most 256 characters. Null otherwise, or when the device gave none."
          }
        },
        "description": "A device command with its payload."
      },
      "CommandPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CommandSummary"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of device commands."
      },
      "CommandPayload": {
        "required": [
          "type"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "The command type of the payload. Known values: `video`, `format`, `reboot`, `firmware_update`, `configuration_update`, `download_logs`, `download_configuration`, `custom_command`, `event_acceleration_data`, `unknown`. More values can be added; handle one you do not know."
          },
          "startAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the requested clip starts (UTC, ISO 8601). Null for types without a start.",
            "format": "date-time"
          },
          "durationSeconds": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The requested clip length in seconds. Null when the type does not use it.",
            "format": "int32"
          },
          "channels": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/Channel"
            },
            "description": "The requested camera channels, with their numbers and labels. Null when the type does not target channels."
          },
          "overlay": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "True when the telemetry overlay was requested on the video. Null when the type does not use it."
          },
          "videoQuality": {
            "type": [
              "null",
              "string"
            ],
            "description": "The requested video stream: `hd` is the main stream and `sd` the sub stream. Null for another command type, or when the request named none. Known values: `sd`, `hd`. More values can be added; handle one you do not know."
          }
        },
        "description": "A command payload. One shape carries the fields of every command type, and each field is null when the type does\nnot use it. Commands without parameters (for example a reboot) have no payload."
      },
      "CommandReference": {
        "required": [
          "id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The command's id. `GET /v1/commands/{commandId}` reads it; for a video request it is also the `mediaRequestId`. An opaque id: treat it as text of up to 255 characters; do not parse it."
          }
        },
        "description": "A reference to a device command by its id. For a video request it is the media request."
      },
      "CommandState": {
        "enum": [
          "queued",
          "dispatched",
          "acknowledged",
          "completed",
          "cancelled",
          "failed",
          "timed_out"
        ],
        "type": "string",
        "description": "The state of a device command or media request. In progress: `queued`, `dispatched`, `acknowledged`. Final: `completed`, `cancelled`, `failed`, `timed_out`. The final states do not change within v1, so poll until `state` is one of them. More in-progress states can be added; treat a state you do not know as in progress.",
        "x-enumDescriptions": {
          "queued": "Waiting to be sent to the device (Queued in the Spillard web app).",
          "dispatched": "Sent to the device (Sent in the web app).",
          "acknowledged": "The device acknowledged the command (Received in the web app).",
          "completed": "The command completed (Done in the web app).",
          "cancelled": "Cancelled before it completed.",
          "failed": "The command failed.",
          "timed_out": "The device did not answer in time; the outcome is unknown (Inconclusive in the web app)."
        }
      },
      "CommandSummary": {
        "required": [
          "id",
          "state",
          "type",
          "organization",
          "device",
          "createdAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The command's id. `GET /v1/commands/{commandId}` reads it. For a video command it is also the id of the media request. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "state": {
            "type": "string",
            "description": "The command's state. The Spillard web app shows `dispatched` as Sent, `acknowledged` as Received, `completed` as Done and `timed_out` as Inconclusive. Known values: `queued`, `dispatched`, `acknowledged`, `completed`, `cancelled`, `failed`, `timed_out`. In progress: `queued`, `dispatched`, `acknowledged`. Final: `completed`, `cancelled`, `failed`, `timed_out`. The final states do not change within v1, so poll until `state` is one of them. More in-progress states can be added; treat a state you do not know as in progress."
          },
          "type": {
            "type": "string",
            "description": "The command's type, for example `video` or `reboot`. It is the type of `payload` in the full command. Known values: `video`, `format`, `reboot`, `firmware_update`, `configuration_update`, `download_logs`, `download_configuration`, `custom_command`, `event_acceleration_data`, `unknown`. More values can be added; handle one you do not know."
          },
          "organization": {
            "description": "The organization that owns the command.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet the command targets. Null when the command is not tied to a fleet."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle the command targets. Null when the command is not tied to a vehicle."
          },
          "event": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/EventReference"
              }
            ],
            "description": "The event the command is linked to: for a video request, the event it belongs to. Null when it is not linked to an event."
          },
          "device": {
            "description": "The device the command was sent to.",
            "$ref": "#/components/schemas/DeviceReference"
          },
          "createdAt": {
            "type": "string",
            "description": "When the command was created (UTC, ISO 8601).",
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the command last changed (UTC, ISO 8601). Null when it has not changed since it was created.",
            "format": "date-time"
          }
        },
        "description": "A device command as lists show it. A video request is a command of type `video`."
      },
      "CommandType": {
        "enum": [
          "video",
          "format",
          "reboot",
          "firmware_update",
          "configuration_update",
          "download_logs",
          "download_configuration",
          "custom_command",
          "event_acceleration_data",
          "unknown"
        ],
        "type": "string",
        "description": "The type of a device command.",
        "x-enumDescriptions": {
          "video": "A video request: a clip from the device's camera.",
          "format": "Format the device's storage.",
          "reboot": "Restart the device.",
          "firmware_update": "Update the device firmware.",
          "configuration_update": "Update the device configuration.",
          "download_logs": "Upload the device logs.",
          "download_configuration": "Upload the device configuration.",
          "custom_command": "A device-specific command.",
          "event_acceleration_data": "Upload the acceleration data of an event.",
          "unknown": "A command without a payload."
        }
      },
      "ConnectivityStatus": {
        "enum": [
          "online",
          "offline",
          "sleep"
        ],
        "type": "string",
        "description": "The connectivity of a device, from the last time Spillard received data from it (a vehicle's is that of its most recently reporting device). The thresholds are those of the Spillard web app's live map.",
        "x-enumDescriptions": {
          "online": "Reported within the last 2 minutes.",
          "offline": "Last reported between 2 minutes and 72 hours ago.",
          "sleep": "Last reported more than 72 hours ago, or never reported. The web app shows it as Inactive."
        }
      },
      "CreateApiClientRequest": {
        "required": [
          "name",
          "scopes",
          "organizationId"
        ],
        "type": "object",
        "properties": {
          "name": {
            "maxLength": 200,
            "minLength": 1,
            "type": "string",
            "description": "The name of the new client, 1 to 200 characters."
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            },
            "description": "The scopes to grant. Each must be one of your own client's scopes."
          },
          "organizationId": {
            "type": "string",
            "description": "The organization to create the client in. Your client must read it with every fleet. Another organization is 403 `auth.tenant_forbidden`. An organization your client reads for some fleets only is 403 `auth.fleet_restricted`. The new client reads this organization only.",
            "format": "uuid"
          }
        },
        "description": "A new API client. `scopes` must be a subset of your client's scopes."
      },
      "CreateDriverRequest": {
        "required": [
          "fleetId",
          "firstName",
          "lastName",
          "phoneNumber"
        ],
        "type": "object",
        "properties": {
          "fleetId": {
            "type": "string",
            "description": "The id of the fleet the driver joins. It must be a fleet you can see: your client reads its organization with every fleet, or the fleet is one of the client's allowed fleets. Otherwise the answer is 422 `validation.reference_not_found`.",
            "format": "uuid"
          },
          "firstName": {
            "maxLength": 50,
            "minLength": 1,
            "type": "string",
            "description": "The driver's given name. Required, 1 to 50 characters, not blank."
          },
          "lastName": {
            "maxLength": 50,
            "minLength": 1,
            "type": "string",
            "description": "The driver's family name. Required, 1 to 50 characters, not blank."
          },
          "phoneNumber": {
            "maxLength": 50,
            "minLength": 1,
            "type": "string",
            "description": "The driver's contact phone number. Required, 1 to 50 characters, not blank."
          }
        },
        "description": "A new driver."
      },
      "CreateEventMediaRequest": {
        "required": [
          "durationSeconds",
          "channels",
          "type"
        ],
        "type": "object",
        "properties": {
          "startAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "The start of the clip (UTC, ISO 8601). The clip lasts `durationSeconds` and ends at `startAt` plus `durationSeconds`. When omitted, the clip is centered on the event: it starts `durationSeconds` / 2 before the event's `triggeredAt`. Send `startAt` equal to `triggeredAt` to start the clip at the event time. A start older than your organization's data access limit (`dataAccessDays` in `GET /v1/context`) is 403 `data.retention_restricted`.",
            "format": "date-time"
          },
          "durationSeconds": {
            "maximum": 120,
            "minimum": 1,
            "type": "integer",
            "description": "The clip length in seconds: 1 to 120. Required; another value is 400 `validation.failed`.",
            "format": "int32"
          },
          "channels": {
            "maxItems": 32,
            "minItems": 1,
            "type": "array",
            "items": {
              "maximum": 32,
              "minimum": 1,
              "type": "integer",
              "format": "int32"
            },
            "description": "The camera channel numbers to capture, for example `[1, 2]`: 1 to 32 numbers, each from 1 to 32. Take them from the `channels` of the device (`GET /v1/devices/{deviceId}`). The API does not check that the device has the channel."
          },
          "type": {
            "description": "The kind of media to capture. Send `video`: a video request, a clip from the device's cameras.",
            "$ref": "#/components/schemas/MediaType"
          },
          "overlay": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "True to burn the on-screen telemetry overlay into the video. Default: false."
          },
          "videoQuality": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/VideoQuality"
              }
            ],
            "description": "The video stream: `hd` is the device's main stream and `sd` its sub stream (the Spillard web app's Main stream and Sub stream). When you omit it, the request names no stream."
          },
          "externalId": {
            "maxLength": 128,
            "type": [
              "null",
              "string"
            ],
            "description": "Your own reference, an opaque text of at most 128 characters. `GET /v1/media-requests/{mediaRequestId}` and the `spillard.media.request.completed.v1` webhook return it exactly as sent."
          }
        },
        "description": "A video request for an event: it asks the device that raised the event for video around the time of the event."
      },
      "CreateFleetRequest": {
        "required": [
          "name",
          "organizationId"
        ],
        "type": "object",
        "properties": {
          "name": {
            "maxLength": 100,
            "minLength": 1,
            "type": "string",
            "description": "The fleet's display name. Required, 1 to 100 characters, not blank."
          },
          "organizationId": {
            "type": "string",
            "description": "The organization to create the fleet in. Your client must read it with every fleet. Another organization is 403 `auth.tenant_forbidden`. An organization your client reads for some fleets only is 403 `auth.fleet_restricted`, because the new fleet would not be one of your fleets.",
            "format": "uuid"
          }
        },
        "description": "A new fleet."
      },
      "CreateMediaRequest": {
        "required": [
          "durationSeconds",
          "channels",
          "type"
        ],
        "type": "object",
        "properties": {
          "hardwareId": {
            "type": [
              "null",
              "string"
            ],
            "description": "The hardware id of the device (its `hardwareId`, not its `serialNumber`). Send exactly one of `hardwareId` and `deviceId`."
          },
          "deviceId": {
            "type": [
              "null",
              "string"
            ],
            "description": "The id (UUID) of the device. Send exactly one of `hardwareId` and `deviceId`.",
            "format": "uuid"
          },
          "startAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "The start of the clip (UTC, ISO 8601). The clip lasts `durationSeconds` and ends at `startAt` plus `durationSeconds`. Send exactly one of `startAt` and `midpointAt`. A start older than your organization's data access limit (`dataAccessDays` in `GET /v1/context`) is 403 `data.retention_restricted`.",
            "format": "date-time"
          },
          "midpointAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "The middle of the clip (UTC, ISO 8601): the clip starts `durationSeconds` / 2 before it and ends `durationSeconds` / 2 after it. Send exactly one of `startAt` and `midpointAt`.",
            "format": "date-time"
          },
          "durationSeconds": {
            "maximum": 120,
            "minimum": 1,
            "type": "integer",
            "description": "The clip length in seconds: 1 to 120. Required; another value is 400 `validation.failed`.",
            "format": "int32"
          },
          "channels": {
            "maxItems": 32,
            "minItems": 1,
            "type": "array",
            "items": {
              "maximum": 32,
              "minimum": 1,
              "type": "integer",
              "format": "int32"
            },
            "description": "The camera channel numbers to capture, for example `[1, 2]`: 1 to 32 numbers, each from 1 to 32. Take them from the `channels` of the device (`GET /v1/devices/{deviceId}`). The API does not check that the device has the channel."
          },
          "type": {
            "description": "The kind of media to capture. Send `video`: a video request, a clip from the device's cameras.",
            "$ref": "#/components/schemas/MediaType"
          },
          "overlay": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "True to burn the on-screen telemetry overlay into the video. Default: false."
          },
          "videoQuality": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/VideoQuality"
              }
            ],
            "description": "The video stream: `hd` is the device's main stream and `sd` its sub stream (the Spillard web app's Main stream and Sub stream). When you omit it, the request names no stream."
          },
          "externalId": {
            "maxLength": 128,
            "type": [
              "null",
              "string"
            ],
            "description": "Your own reference, an opaque text of at most 128 characters. `GET /v1/media-requests/{mediaRequestId}` and the `spillard.media.request.completed.v1` webhook return it exactly as sent."
          }
        },
        "description": "A video request for a device. Name the device with exactly one of `hardwareId` and `deviceId`, and the time\nwith exactly one of `startAt` (the start of the clip) and `midpointAt` (its middle). Otherwise the answer is\n400 `validation.failed`."
      },
      "CreateVehicleRequest": {
        "required": [
          "fleetId",
          "registrationNumber",
          "make",
          "model"
        ],
        "type": "object",
        "properties": {
          "fleetId": {
            "type": "string",
            "description": "The id of the fleet to create the vehicle in. It must be a fleet you can see: your client reads its organization with every fleet, or the fleet is one of the client's allowed fleets. Otherwise the answer is 422 `validation.reference_not_found`.",
            "format": "uuid"
          },
          "registrationNumber": {
            "maxLength": 50,
            "minLength": 1,
            "type": "string",
            "description": "The registration number (number plate). Required, 1 to 50 characters, not blank, unique within the fleet's organization."
          },
          "make": {
            "maxLength": 50,
            "minLength": 1,
            "type": "string",
            "description": "The make (manufacturer). Required, 1 to 50 characters, not blank."
          },
          "model": {
            "maxLength": 50,
            "minLength": 1,
            "type": "string",
            "description": "The model name. Required, 1 to 50 characters, not blank."
          },
          "fuelType": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/FuelType"
              }
            ],
            "description": "The fuel type. Default: `other`. An unknown value is 400 `validation.failed`."
          },
          "engineSizeLiters": {
            "minimum": 0,
            "type": [
              "null",
              "number"
            ],
            "description": "The engine size in liters, 0 or more. Null when unknown. A negative value is 400 `validation.failed`.",
            "format": "double"
          },
          "vehicleType": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/VehicleType"
              }
            ],
            "description": "The vehicle type. Default: `unknown`. An unknown value is 400 `validation.failed`."
          },
          "chassisNumber": {
            "maxLength": 100,
            "type": [
              "null",
              "string"
            ],
            "description": "The chassis number (VIN), at most 100 characters. Null for none."
          },
          "yearOfManufacture": {
            "minimum": 1950,
            "type": [
              "null",
              "integer"
            ],
            "description": "The year the vehicle was made, from 1950 to the current year. Null when unknown. Another year is 400 `validation.failed`.",
            "format": "int32"
          },
          "enteredMileage": {
            "maximum": 100000000,
            "minimum": 0,
            "type": [
              "null",
              "number"
            ],
            "description": "The vehicle's mileage as you record it, from 0 to 100,000,000. Spillard stores no unit. Null for none. Another value is 400 `validation.failed`.",
            "format": "double"
          }
        },
        "description": "A new vehicle."
      },
      "CreateWebhookEndpointRequest": {
        "required": [
          "name",
          "url",
          "eventTypes"
        ],
        "type": "object",
        "properties": {
          "name": {
            "maxLength": 64,
            "minLength": 1,
            "type": "string",
            "description": "The endpoint's name, 1 to 64 characters, unique within the organization."
          },
          "url": {
            "type": "string",
            "description": "The HTTPS URL Spillard sends signed messages to with POST. It must be a public address.",
            "format": "uri"
          },
          "eventTypes": {
            "minItems": 1,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "description": "The webhook types to subscribe to, for example `spillard.event.raised.v1`: at least one. These are not device event types such as `dsm.fatigue`; narrow those with `eventTypeFilters`. One active endpoint of the organization can subscribe to each webhook type."
          },
          "eventTypeFilters": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/EventTypeFilter"
            },
            "description": "Narrows the `spillard.event.raised.v1` messages to these device event types, for example `adas.*` or `dsm.fatigue`. Allowed only when `eventTypes` includes `spillard.event.raised.v1`. Omit it to deliver every device event."
          },
          "active": {
            "type": "boolean",
            "description": "True when the endpoint starts active and receives deliveries. Default: true.",
            "default": true
          },
          "includeSubOrganizations": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "True when the endpoint also receives the events of every sub-organization of the endpoint's organization (yours, or the one `organizationId` names). Default: true when your client may read that organization's sub-organizations (its entry in `access` of `GET /v1/context` has `includeSubOrganizations: true`), otherwise false. `true` without that permission is 403 `auth.tenant_forbidden`."
          }
        },
        "description": "A new webhook endpoint."
      },
      "Device": {
        "required": [
          "id",
          "hardwareId",
          "enabled",
          "organization",
          "channels"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The device's id (UUID). Use it as `{deviceId}` in paths.",
            "format": "uuid"
          },
          "hardwareId": {
            "type": "string",
            "description": "The hardware id of the device: the identifier it reports itself with. It is not the `serialNumber`. `{deviceId}` in paths accepts it too."
          },
          "enabled": {
            "type": "boolean",
            "description": "True when Spillard accepts data from the device. `:disable-ingest` stops it and `:enable-ingest` resumes it."
          },
          "model": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DeviceModelReference"
              }
            ],
            "description": "The device's model. Null when none is assigned."
          },
          "organization": {
            "description": "The organization that owns the device.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet the device is assigned to. Null when it is not assigned to a fleet."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle the device is fitted to. Null when it is not fitted to a vehicle."
          },
          "firmwareVersion": {
            "type": [
              "null",
              "string"
            ],
            "description": "The device's firmware version. Null when unknown. Some device models report it when they connect. For the others it is entered by hand."
          },
          "simNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The number of the SIM card in the device. Null when unknown."
          },
          "serialNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The device's serial number. It is a separate field from `hardwareId`. Null when unknown."
          },
          "protocol": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DeviceProtocolReference"
              }
            ],
            "description": "The protocol the device speaks. Null when none is assigned."
          },
          "channels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Channel"
            },
            "description": "The camera channels of the device, with their labels. Use these numbers in the `channels` of a video request. Empty when the device has no camera channels."
          },
          "connectivityStatus": {
            "type": [
              "null",
              "string"
            ],
            "description": "The device's connectivity, from the time of its last data: `online` within 2 minutes, `offline` up to 72 hours, `sleep` after that or when it never reported (the Spillard web app's live map shows `sleep` as Inactive). Null when it cannot be determined (the device has no hardware id, or live data is temporarily unavailable). Known values: `online`, `offline`, `sleep`. More values can be added; handle one you do not know."
          },
          "lastReportedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When Spillard last received data from the device (UTC, ISO 8601). Null when it never reported or live data is temporarily unavailable.",
            "format": "date-time"
          },
          "firstReportedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When Spillard first received data from the device (UTC, ISO 8601). Null when it never reported.",
            "format": "date-time"
          }
        },
        "description": "A tracking or camera device with its details."
      },
      "DeviceEvent": {
        "required": [
          "id",
          "eventTypes",
          "categories",
          "classification",
          "triggeredAt",
          "receivedAt",
          "device",
          "organization",
          "hasMedia",
          "origin",
          "mediaStatus"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The event's id. `GET /v1/events/{eventId}` and the other reads of one event take it. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "eventTypes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What happened, as device event types in the form `family.name`, for example `driver_behaviour.harsh_braking`. These are not webhook types (`spillard.*.v1`). Examples: `adas.bridge_detected`, `adas.bridge_recognition`, `adas.bridge_stop`. See the `EventType` model for the values known today. More values can be added; handle one you do not know."
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The categories the event types belong to, for example `driver_behaviour`. Known values: `none`, `driver_behaviour`, `safety_and_security`, `driver_state_monitoring`, `vehicle_state_monitoring`, `adas`, `diagnostics`. More values can be added; handle one you do not know."
          },
          "classification": {
            "type": "string",
            "description": "The event's classification: `low`, `medium` or `high` severity (green, amber or red in the Spillard web app), `requested` for the event of a video request, or `unclassified`. Never null. Known values: `unclassified`, `requested`, `low`, `medium`, `high`. More values can be added; handle one you do not know."
          },
          "vendorEventType": {
            "type": [
              "null",
              "string"
            ],
            "description": "The device vendor's own event type, when the device sent one. Null otherwise."
          },
          "triggeredAt": {
            "type": "string",
            "description": "When the event was triggered on the device (UTC, ISO 8601).",
            "format": "date-time"
          },
          "receivedAt": {
            "type": "string",
            "description": "When Spillard received the event (UTC, ISO 8601).",
            "format": "date-time"
          },
          "device": {
            "description": "The device that raised the event.",
            "$ref": "#/components/schemas/DeviceReference"
          },
          "organization": {
            "description": "The organization that owns the event.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet the device belonged to when the event was triggered. Null when it had none."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/EventVehicleReference"
              }
            ],
            "description": "The vehicle the device was fitted to when the event was triggered. Null when it was not fitted."
          },
          "driver": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The driver assigned to the vehicle when the event was triggered. Null when there was none. Spillard does not know who is driving."
          },
          "hasMedia": {
            "type": "boolean",
            "description": "True when media files are available for the event. It is the same as `mediaStatus``available`."
          },
          "locations": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/GeoPoint"
            },
            "description": "The positions recorded during the event. Null when the event has no location trail."
          },
          "address": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Address"
              }
            ],
            "description": "The address at the event location. Null when none was found."
          },
          "speedLimitKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The posted speed limit at the event location in km/h. Null when unknown.",
            "format": "int32"
          },
          "speedPointsKph": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "integer",
              "format": "int32"
            },
            "description": "The speed readings across the event in km/h. Null when none were captured, and in the `basic` view of a list."
          },
          "headingPointsDegrees": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "integer",
              "format": "int32"
            },
            "description": "The heading readings across the event in degrees, 0 to 359. Null when none were captured, and in the `basic` view of a list."
          },
          "gSensorFrames": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/GSensorFrame"
            },
            "description": "The accelerometer samples of the event. Null when the device sent none, and in the `basic` view of a list."
          },
          "gyroscopeFrames": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/GyroscopeFrame"
            },
            "description": "The gyroscope samples of the event. Null when the device sent none, and in the `basic` view of a list."
          },
          "speedKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The vehicle's speed when the event happened in km/h. It is the middle reading of `speedPointsKph`. Null when the device sent no speed.",
            "format": "int32"
          },
          "maxSpeedKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The highest speed across the event in km/h. Null when the device sent no speed.",
            "format": "int32"
          },
          "firmwareVersion": {
            "type": [
              "null",
              "string"
            ],
            "description": "The device's firmware version when the event was raised. Null when unknown."
          },
          "origin": {
            "type": "string",
            "description": "What created the event: the device, an alarm, or a video request. It is the same value as in the `spillard.event.raised.v1` webhook. Known values: `device`, `alarm`, `request`. More values can be added; handle one you do not know."
          },
          "mediaStatus": {
            "type": "string",
            "description": "Where the event's media stands. `available`: media arrived (the same as `hasMedia`). `pending`: no media yet, and a video request for the event is waiting, sent or received by the device. `none`: neither. The `mediaExpected` field of the `spillard.event.raised.v1` webhook says in advance that a video request for the event will follow. Known values: `none`, `pending`, `available`. More values can be added; handle one you do not know."
          },
          "share": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/EventShareLink"
              }
            ],
            "description": "A public link to the event's page in the Spillard web app, which shows the event and its media without a sign-in. It is returned only when the read asked for `include=shareUrl`. It is null otherwise, when the event's fleet (or its device's fleet) requires a video audit form, and when the page would show data outside your access."
          }
        },
        "description": "An event a device raised. `eventTypes` says what happened; `classification` how severe it was."
      },
      "DeviceEventPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceEvent"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/TimeWindowMeta"
              }
            ],
            "description": "The time windows the list used. Null when not available."
          }
        },
        "description": "A page of device events, with `meta`: the time windows the list used."
      },
      "DeviceModel": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The device model's id.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The model's name."
          }
        },
        "description": "A device model Spillard supports."
      },
      "DeviceModelPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceModel"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of device models."
      },
      "DeviceModelReference": {
        "required": [
          "id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The device model's id.",
            "format": "uuid"
          },
          "name": {
            "type": [
              "null",
              "string"
            ],
            "description": "The model's name. Null when the model has none."
          }
        },
        "description": "A reference to a device model."
      },
      "DevicePage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceSummary"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of devices."
      },
      "DevicePosition": {
        "required": [
          "device",
          "recordedAt",
          "location",
          "gpsValid",
          "organization"
        ],
        "type": "object",
        "properties": {
          "device": {
            "description": "The reporting device.",
            "$ref": "#/components/schemas/DeviceReference"
          },
          "recordedAt": {
            "type": "string",
            "description": "When the device recorded this position (UTC, ISO 8601).",
            "format": "date-time"
          },
          "location": {
            "description": "The device's last valid location.",
            "$ref": "#/components/schemas/GeoPoint"
          },
          "speedKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The ground speed in km/h. Null when not reported.",
            "format": "int32"
          },
          "headingDegrees": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The heading in degrees, 0 to 359. Null when not reported.",
            "format": "int32"
          },
          "gpsValid": {
            "type": "boolean",
            "description": "False when the location is `0,0` because no fix was ever stored. Otherwise true. The location can be older than `recordedAt`."
          },
          "organization": {
            "description": "The organization that owns the device.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet the device is assigned to. Null when it is not assigned to a fleet."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle the device is fitted to. Null when it is not fitted to a vehicle."
          },
          "driver": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The driver assigned to the vehicle. Null when there is none. Spillard does not know who is driving."
          }
        },
        "description": "A device's latest position. Spillard keeps valid GPS fixes only. When the device reports without a fix, the\nprevious location stays and `recordedAt` is the time of the latest report."
      },
      "DeviceProtocol": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The device protocol's id.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The protocol's name."
          }
        },
        "description": "A communication protocol of the devices Spillard supports."
      },
      "DeviceProtocolPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceProtocol"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of device protocols."
      },
      "DeviceProtocolReference": {
        "required": [
          "id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The device protocol's id.",
            "format": "uuid"
          },
          "name": {
            "type": [
              "null",
              "string"
            ],
            "description": "The protocol's name. Null when the protocol has none."
          }
        },
        "description": "A reference to a device protocol (`GET /v1/device-protocols/{deviceProtocolId}`)."
      },
      "DeviceReference": {
        "required": [
          "hardwareId"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "null",
              "string"
            ],
            "description": "The device's id (UUID). Use it as `{deviceId}` in paths. Null when none of your organizations has a device with this hardware id any more.",
            "format": "uuid"
          },
          "hardwareId": {
            "type": "string",
            "description": "The hardware id of the device: the identifier it reports itself with. It is not the `serialNumber`."
          }
        },
        "description": "A reference to a device: its id (the `{deviceId}` of `/v1/devices`) and its hardware id. `id` is null when no device of your organizations has the hardware id any more; webhooks leave the field out in that case."
      },
      "DeviceSummary": {
        "required": [
          "id",
          "hardwareId",
          "enabled",
          "organization",
          "channels"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The device's id (UUID). Use it as `{deviceId}` in paths.",
            "format": "uuid"
          },
          "hardwareId": {
            "type": "string",
            "description": "The hardware id of the device: the identifier it reports itself with. It is not the `serialNumber`. `{deviceId}` in paths accepts it too."
          },
          "enabled": {
            "type": "boolean",
            "description": "True when Spillard accepts data from the device. `:disable-ingest` stops it and `:enable-ingest` resumes it."
          },
          "model": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DeviceModelReference"
              }
            ],
            "description": "The device's model. Null when none is assigned."
          },
          "organization": {
            "description": "The organization that owns the device.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet the device is assigned to. Null when it is not assigned to a fleet."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle the device is fitted to. Null when it is not fitted to a vehicle."
          },
          "firmwareVersion": {
            "type": [
              "null",
              "string"
            ],
            "description": "The device's firmware version. Null when unknown. Some device models report it when they connect. For the others it is entered by hand."
          },
          "simNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The number of the SIM card in the device. Null when unknown."
          },
          "serialNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The device's serial number. It is a separate field from `hardwareId`. Null when unknown."
          },
          "protocol": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DeviceProtocolReference"
              }
            ],
            "description": "The protocol the device speaks. Null when none is assigned."
          },
          "channels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Channel"
            },
            "description": "The camera channels of the device, with their labels. Use these numbers in the `channels` of a video request. Empty when the device has no camera channels."
          },
          "connectivityStatus": {
            "type": [
              "null",
              "string"
            ],
            "description": "The device's connectivity, from the time of its last data: `online` within 2 minutes, `offline` up to 72 hours, `sleep` after that or when it never reported (the Spillard web app's live map shows `sleep` as Inactive). Null when it cannot be determined (the device has no hardware id, or live data is temporarily unavailable). Known values: `online`, `offline`, `sleep`. More values can be added; handle one you do not know."
          },
          "lastReportedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When Spillard last received data from the device (UTC, ISO 8601). Null when it never reported or live data is temporarily unavailable.",
            "format": "date-time"
          },
          "firstReportedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When Spillard first received data from the device (UTC, ISO 8601). Null when it never reported.",
            "format": "date-time"
          }
        },
        "description": "A device as lists show it."
      },
      "Driver": {
        "required": [
          "id",
          "organization"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The driver's id. It does not change.",
            "format": "uuid"
          },
          "firstName": {
            "type": [
              "null",
              "string"
            ],
            "description": "The driver's given name. Null when not recorded."
          },
          "lastName": {
            "type": [
              "null",
              "string"
            ],
            "description": "The driver's family name. Null when not recorded."
          },
          "name": {
            "type": [
              "null",
              "string"
            ],
            "description": "The driver's full display name. Null when not recorded."
          },
          "organization": {
            "description": "The organization the driver belongs to.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet the driver belongs to. Null when the driver is not in a fleet."
          },
          "phoneNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The driver's contact phone number. Null when not recorded."
          },
          "currentVehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle the driver is assigned to now. Null when the driver has no vehicle."
          }
        },
        "description": "A driver with the vehicle they drive."
      },
      "DriverPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DriverSummary"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of drivers."
      },
      "DriverSummary": {
        "required": [
          "id",
          "organization"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The driver's id. It does not change.",
            "format": "uuid"
          },
          "firstName": {
            "type": [
              "null",
              "string"
            ],
            "description": "The driver's given name. Null when not recorded."
          },
          "lastName": {
            "type": [
              "null",
              "string"
            ],
            "description": "The driver's family name. Null when not recorded."
          },
          "name": {
            "type": [
              "null",
              "string"
            ],
            "description": "The driver's full display name. Null when not recorded."
          },
          "organization": {
            "description": "The organization the driver belongs to.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet the driver belongs to. Null when the driver is not in a fleet."
          }
        },
        "description": "A driver as lists show it."
      },
      "ErrorCode": {
        "enum": [
          "api_client.not_found",
          "audit_form.required",
          "auth.fleet_restricted",
          "auth.scope_missing",
          "auth.tenant_forbidden",
          "auth.token_invalid",
          "command.not_found",
          "concurrency.etag_mismatch",
          "data.retention_restricted",
          "dependency.timeout",
          "dependency.unavailable",
          "device.command_not_supported",
          "device.not_found",
          "device_model.not_found",
          "device_protocol.not_found",
          "driver.not_found",
          "event.not_found",
          "event.share_restricted",
          "fleet.not_found",
          "idempotency.in_progress",
          "idempotency.key_conflict",
          "journey.not_found",
          "organization.not_found",
          "precondition.required",
          "rate.limit_exceeded",
          "request.unsupported_media_type",
          "resource.link_conflict",
          "server.not_implemented",
          "server.unexpected",
          "validation.cursor_invalid",
          "validation.failed",
          "validation.reference_not_found",
          "vehicle.already_in_fleet",
          "vehicle.driver_fleet_mismatch",
          "vehicle.not_found",
          "webhook_delivery.not_found",
          "webhook_delivery.not_replayable",
          "webhook_endpoint.conflict",
          "webhook_endpoint.limit_exceeded",
          "webhook_endpoint.not_found"
        ],
        "type": "string",
        "description": "The `errorCode` of an error, with what it means. `errorCode` itself is a string, so a client keeps a code that is added within v1; handle a code you do not know by the HTTP status.",
        "x-enumDescriptions": {
          "api_client.not_found": "The API client does not exist or is outside your client's access. Check the id.",
          "audit_form.required": "The fleet requires a video audit form for this action, which the API cannot submit. Do it in the Spillard web app.",
          "auth.fleet_restricted": "Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
          "auth.scope_missing": "The token lacks the scope the operation requires, or the request names a scope your client does not hold. Grant the scope to the API client and get a new token.",
          "auth.tenant_forbidden": "The organization is outside your client's access, the request needs sub-organization access your client lacks, or the token's access list is damaged. Check `access` in `GET /v1/context`, or ask your Spillard account manager to change your client's access.",
          "auth.token_invalid": "The bearer token is missing, malformed, expired or issued for another API. Get a new token.",
          "command.not_found": "The command or media request does not exist or is outside your client's access. Check the id. The media request routes answer with this code too.",
          "concurrency.etag_mismatch": "The resource changed since you read it: `If-Match` no longer matches its `ETag`. Read it again, then retry the update.",
          "data.retention_restricted": "The data is older than the organization's data access allows (see `dataAccessDays` in `GET /v1/context`). Ask for newer data.",
          "dependency.timeout": "A Spillard service did not answer in time. Retry with backoff.",
          "dependency.unavailable": "A Spillard service is temporarily unavailable. Retry with backoff.",
          "device.command_not_supported": "The device does not support the requested command. Choose a command the device supports.",
          "device.not_found": "The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.",
          "device_model.not_found": "No device model has this id. Check the id.",
          "device_protocol.not_found": "No device protocol has this id. Check the id.",
          "driver.not_found": "The driver does not exist or is outside your client's access. Check the id. On a vehicle route it can also mean the driver does not drive that vehicle.",
          "event.not_found": "The event does not exist or is outside your client's access. Check the id.",
          "event.share_restricted": "The event cannot be shared by link: its page would show data outside what your client may read. Do not share this event, or ask your Spillard account manager for wider access for your client.",
          "fleet.not_found": "The fleet does not exist or is outside your client's access. Check the id.",
          "idempotency.in_progress": "The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.",
          "idempotency.key_conflict": "The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
          "journey.not_found": "The journey does not exist or is outside your client's access. Check the id.",
          "organization.not_found": "The organization does not exist or is outside your client's access. Check the id.",
          "precondition.required": "A required header is missing: `If-Match` when you update a vehicle or a driver, `Idempotency-Key` when you create a media request. Send it and retry.",
          "rate.limit_exceeded": "The API client sent more requests than its rate limit allows. Wait for `Retry-After` seconds, then retry.",
          "request.unsupported_media_type": "The body is not JSON. Send it with `Content-Type: application/json`.",
          "resource.link_conflict": "The link cannot be made as asked: the vehicle already has a driver, the driver or device is linked to another vehicle, the two are in different organizations or fleets, or the registration number is taken in the target organization. Unassign the other link or change the target first.",
          "server.not_implemented": "The operation is not available on this host. Do not retry; quote `traceId` to Spillard support.",
          "server.unexpected": "An unexpected error. Retry with backoff; quote `traceId` to Spillard support if it persists.",
          "validation.cursor_invalid": "The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.",
          "validation.failed": "A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
          "validation.reference_not_found": "An id in the body (for example `fleetId`) does not exist or is outside your client's access. Send an id your client may read.",
          "vehicle.already_in_fleet": "The vehicle is in the target fleet already, so nothing would change. Choose another fleet.",
          "vehicle.driver_fleet_mismatch": "The vehicle and the driver are in different fleets. Use a driver of the vehicle's fleet, or move the vehicle first.",
          "vehicle.not_found": "The vehicle does not exist or is outside your client's access. Check the id.",
          "webhook_delivery.not_found": "No pending or failed delivery with this id belongs to the endpoint; delivered messages are not listed. Check the id in the delivery list.",
          "webhook_delivery.not_replayable": "The delivery cannot be replayed now: the endpoint does not receive its type any more, the message was not kept in full, or a delivery of the same message is pending. Check the endpoint settings, or wait for the pending delivery.",
          "webhook_endpoint.conflict": "The endpoint name is taken, a webhook type is subscribed by another active endpoint, or the endpoint has no usable signing secret. Change the name or the types, or issue a secret with `:rotate-secret`.",
          "webhook_endpoint.limit_exceeded": "The organization has the maximum number of webhook endpoints (`endpointLimit` in `GET /v1/webhook-settings`) already. Delete an endpoint first.",
          "webhook_endpoint.not_found": "No webhook endpoint with this id belongs to the organization. Check the id."
        }
      },
      "EventCategory": {
        "enum": [
          "none",
          "driver_behaviour",
          "safety_and_security",
          "driver_state_monitoring",
          "vehicle_state_monitoring",
          "adas",
          "diagnostics"
        ],
        "type": "string",
        "description": "The category an event type rolls up into.",
        "x-enumDescriptions": {
          "none": "No category.",
          "driver_behaviour": "Driver behavior (harsh driving, speeding).",
          "safety_and_security": "Safety and security (panic button, emergency, alarms).",
          "driver_state_monitoring": "Driver state monitoring (fatigue, distraction, phone use).",
          "vehicle_state_monitoring": "Vehicle state monitoring (ignition, indicators, reversing).",
          "adas": "Advanced driver assistance systems (collision and lane warnings, human detection).",
          "diagnostics": "Device diagnostics (camera, GPS and storage faults)."
        }
      },
      "EventClassification": {
        "enum": [
          "unclassified",
          "requested",
          "low",
          "medium",
          "high"
        ],
        "type": "string",
        "description": "The classification of a device event, the same vocabulary as in webhooks. Never null.",
        "x-enumDescriptions": {
          "unclassified": "Not classified.",
          "requested": "Video someone asked for (a video request).",
          "low": "Low severity (green in the Spillard web app).",
          "medium": "Medium severity (amber in the Spillard web app).",
          "high": "High severity (red in the Spillard web app)."
        }
      },
      "EventInclude": {
        "enum": [
          "shareUrl"
        ],
        "type": "string",
        "description": "What an event read adds to each event on request.",
        "x-enumDescriptions": {
          "shareUrl": "`share`: a public link to the event's page in the Spillard web app, working for 30 days. It shows the event and its media without a sign-in, so it needs the `media.read` scope too."
        }
      },
      "EventLinks": {
        "required": [
          "self",
          "media"
        ],
        "type": "object",
        "properties": {
          "self": {
            "type": "string",
            "description": "The event: `/v1/events/{eventId}`.",
            "format": "uri-reference"
          },
          "media": {
            "type": "string",
            "description": "The event's media files: `/v1/events/{eventId}/media`.",
            "format": "uri-reference"
          }
        },
        "description": "Connect API links of an event, as paths on the API host (for example `https://connect.spillard.live`)."
      },
      "EventOrder": {
        "enum": [
          "triggered_at_desc",
          "triggered_at_asc",
          "received_at_desc",
          "received_at_asc"
        ],
        "type": "string",
        "description": "The order of an event list: the time axis and the direction. Each order pages by a cursor on its time axis.",
        "x-enumDescriptions": {
          "triggered_at_desc": "Newest trigger time first (the default).",
          "triggered_at_asc": "Oldest trigger time first.",
          "received_at_desc": "Newest received time first (the default when only a received window is given).",
          "received_at_asc": "Oldest received time first: for an incremental sync (without a window, the last 24 hours of received time)."
        }
      },
      "EventOrigin": {
        "enum": [
          "device",
          "alarm",
          "request"
        ],
        "type": "string",
        "description": "What created an event.",
        "x-enumDescriptions": {
          "device": "The device reported it.",
          "alarm": "Spillard created it from an alarm.",
          "request": "A video request created it."
        }
      },
      "EventPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceEvent"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/TimeWindowMeta"
              }
            ],
            "description": "The time windows the list used. Null when not available."
          }
        },
        "description": "A page of events, with `meta`: the time windows the list used. This list has no `totalCount`."
      },
      "EventRaisedData": {
        "required": [
          "eventId",
          "triggeredAt",
          "receivedAt",
          "eventTypes",
          "classification",
          "origin",
          "mediaExpected",
          "organization",
          "device",
          "links"
        ],
        "type": "object",
        "properties": {
          "eventId": {
            "type": "string",
            "description": "The event's id: `GET /v1/events/{eventId}` reads it. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "triggeredAt": {
            "type": "string",
            "description": "When the event triggered on the device (UTC, ISO 8601).",
            "format": "date-time"
          },
          "receivedAt": {
            "type": "string",
            "description": "When Spillard received the event (UTC, ISO 8601).",
            "format": "date-time"
          },
          "eventTypes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The device event types as `family.name` tokens (for example `dsm.fatigue`). These are not webhook types (`spillard.*.v1`). Examples: `adas.bridge_detected`, `adas.bridge_recognition`, `adas.bridge_stop`. See the `EventType` model for the values known today. More values can be added; handle one you do not know."
          },
          "classification": {
            "type": "string",
            "description": "The classification, never null (the same vocabulary as the Connect API's events). Known values: `unclassified`, `requested`, `low`, `medium`, `high`. More values can be added; handle one you do not know."
          },
          "origin": {
            "type": "string",
            "description": "What created the event: the device, an alarm or a video request. Known values: `device`, `alarm`, `request`. More values can be added; handle one you do not know."
          },
          "location": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/GeoPoint"
              }
            ],
            "description": "Where the device was when the event triggered; null without a position."
          },
          "address": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/WebhookAddress"
              }
            ],
            "description": "The street address of `location`; null when unknown."
          },
          "speedKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "Speed in km/h when the event triggered; null when unknown.",
            "format": "int32"
          },
          "maxSpeedKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The highest speed in km/h around the event; null when unknown.",
            "format": "int32"
          },
          "speedLimitKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The speed limit at `location` in km/h; null when unknown.",
            "format": "int32"
          },
          "headingDegrees": {
            "type": [
              "null",
              "integer"
            ],
            "description": "Heading in degrees (0 to 359, clockwise from north) when the event triggered; null when unknown.",
            "format": "int32"
          },
          "mediaExpected": {
            "type": "boolean",
            "description": "True when Spillard will request video for the event. A `spillard.media.request.completed.v1` message with `trigger``event` and the same `eventId` then reports the outcome. The REST event shows the state of its media as `mediaStatus` and `hasMedia`."
          },
          "firmwareVersion": {
            "type": [
              "null",
              "string"
            ],
            "description": "The device's firmware version when it raised the event; null when unknown."
          },
          "organization": {
            "description": "The organization the event belongs to (also when the message reaches an endpoint of an organization above it).",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet of the device's vehicle, or null when it has none."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/WebhookVehicleReference"
              }
            ],
            "description": "The vehicle the device is fitted to, or null when it is not fitted to one."
          },
          "driver": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle's driver, or null when none is known."
          },
          "device": {
            "description": "The device that raised the event.",
            "$ref": "#/components/schemas/DeviceReference"
          },
          "links": {
            "description": "Where to read the event and its media files in the Connect API.",
            "$ref": "#/components/schemas/EventLinks"
          }
        },
        "description": "The `data` of `spillard.event.raised.v1`: a new event raised by a device (harsh driving, fatigue, panic\nbutton and more). The envelope's `subject` is `events/{eventId}` and its `time` is `triggeredAt`."
      },
      "EventRaisedMessage": {
        "required": [
          "id",
          "type",
          "source",
          "time",
          "data",
          "specversion",
          "datacontenttype"
        ],
        "type": "object",
        "properties": {
          "specversion": {
            "const": "1.0",
            "type": "string",
            "description": "The CloudEvents specification version, always `1.0`."
          },
          "id": {
            "type": "string",
            "description": "The message id, equal to the `webhook-id` header. A retry keeps the id: deduplicate on it. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "type": {
            "const": "spillard.event.raised.v1",
            "type": "string",
            "description": "The webhook type, always `spillard.event.raised.v1` for this message."
          },
          "source": {
            "type": "string",
            "description": "The organization the fact belongs to: `/organizations/{organizationId}`."
          },
          "subject": {
            "type": [
              "null",
              "string"
            ],
            "description": "The entity the fact is about (for example `events/{eventId}`); absent from the `telemetry.live` and `telemetry.history` batches."
          },
          "time": {
            "type": "string",
            "description": "When the fact occurred (UTC, milliseconds).",
            "format": "date-time"
          },
          "datacontenttype": {
            "const": "application/json",
            "type": "string",
            "description": "The media type of `data`, always `application/json`."
          },
          "data": {
            "description": "The payload of the message.",
            "$ref": "#/components/schemas/EventRaisedData"
          }
        },
        "description": "The `spillard.event.raised.v1` webhook message: a CloudEvents 1.0 envelope in structured mode whose `data` carries the payload. Within v1 the envelope and payload only gain fields: tolerate unknown fields."
      },
      "EventReference": {
        "required": [
          "id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The event's id. `GET /v1/events/{eventId}` reads it. An opaque id: treat it as text of up to 255 characters; do not parse it."
          }
        },
        "description": "A reference to an event by its id."
      },
      "EventShare": {
        "required": [
          "id",
          "url",
          "expiresAt",
          "event"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The share's id (UUID).",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "description": "The public link. Anyone holding it sees the event and its media without signing in until `expiresAt`. The link cannot be revoked before it expires; treat it as a secret.",
            "format": "uri"
          },
          "expiresAt": {
            "type": "string",
            "description": "When the link stops working (UTC, ISO 8601): the expiry you asked for, or 30 days ahead. For an organization limited to the last 30 days of data, it is cut to the day the event turns 30 days old.",
            "format": "date-time"
          },
          "event": {
            "description": "The shared event.",
            "$ref": "#/components/schemas/EventReference"
          }
        },
        "description": "A share of an event: a public link to the event's page in the Spillard web app. The share is also listed with the\nshared events of the event's organization in the web app."
      },
      "EventShareLink": {
        "required": [
          "url",
          "expiresAt"
        ],
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "The link to the event's page in the Spillard web app.",
            "format": "uri"
          },
          "expiresAt": {
            "type": "string",
            "description": "When the link stops working (UTC, ISO 8601).",
            "format": "date-time"
          }
        },
        "description": "A public link to an event's page in the Spillard web app. Anyone holding the link sees the event and its media\nwithout signing in until `expiresAt`. The link cannot be revoked before it expires; treat it as a secret."
      },
      "EventType": {
        "enum": [
          "adas.bridge_detected",
          "adas.bridge_recognition",
          "adas.bridge_stop",
          "adas.following_distance_warning",
          "adas.forward_collision_warning",
          "adas.human_detection",
          "adas.human_detection_error",
          "adas.lane_departure",
          "diagnostics.camera_covered",
          "diagnostics.general",
          "diagnostics.gps_fault",
          "diagnostics.storage_abnormal",
          "diagnostics.system",
          "diagnostics.video_loss",
          "driver_behaviour.harsh_acceleration",
          "driver_behaviour.harsh_braking",
          "driver_behaviour.harsh_cornering",
          "driver_behaviour.shock",
          "driver_behaviour.speeding",
          "dsm.distraction",
          "dsm.fatigue",
          "dsm.mobile_phone",
          "dsm.no_driver",
          "dsm.smoking",
          "requested_data.video",
          "safety.emergency_999",
          "safety.external_alarms_off",
          "safety.panic_button",
          "safety.thumbs_up",
          "vehicle_state.ignition_off",
          "vehicle_state.ignition_on",
          "vehicle_state.indicator_left",
          "vehicle_state.indicator_right",
          "vehicle_state.reversing"
        ],
        "type": "string",
        "description": "The type of a device event, as a `family.name` token (for example `dsm.fatigue`). It is not a webhook type.",
        "x-enumDescriptions": {
          "adas.bridge_detected": "Driver assistance (ADAS): bridge detected.",
          "adas.bridge_recognition": "Driver assistance (ADAS): bridge recognition.",
          "adas.bridge_stop": "Driver assistance (ADAS): bridge stop.",
          "adas.following_distance_warning": "Driver assistance (ADAS): following distance warning.",
          "adas.forward_collision_warning": "Driver assistance (ADAS): forward collision warning.",
          "adas.human_detection": "Driver assistance (ADAS): human detection.",
          "adas.human_detection_error": "Driver assistance (ADAS): human detection error.",
          "adas.lane_departure": "Driver assistance (ADAS): lane departure.",
          "diagnostics.camera_covered": "Device diagnostics: camera covered.",
          "diagnostics.general": "Device diagnostics: general.",
          "diagnostics.gps_fault": "Device diagnostics: gps fault.",
          "diagnostics.storage_abnormal": "Device diagnostics: storage abnormal.",
          "diagnostics.system": "Device diagnostics: system.",
          "diagnostics.video_loss": "Device diagnostics: video loss.",
          "driver_behaviour.harsh_acceleration": "Driver behavior: harsh acceleration.",
          "driver_behaviour.harsh_braking": "Driver behavior: harsh braking.",
          "driver_behaviour.harsh_cornering": "Driver behavior: harsh cornering.",
          "driver_behaviour.shock": "Driver behavior: shock.",
          "driver_behaviour.speeding": "Driver behavior: speeding.",
          "dsm.distraction": "Driver state monitoring: distraction.",
          "dsm.fatigue": "Driver state monitoring: fatigue.",
          "dsm.mobile_phone": "Driver state monitoring: mobile phone.",
          "dsm.no_driver": "Driver state monitoring: no driver.",
          "dsm.smoking": "Driver state monitoring: smoking.",
          "requested_data.video": "Video request: the event Spillard records for a video request.",
          "safety.emergency_999": "Safety: emergency 999.",
          "safety.external_alarms_off": "Safety: external alarms off.",
          "safety.panic_button": "Safety: panic button.",
          "safety.thumbs_up": "Safety: thumbs up.",
          "vehicle_state.ignition_off": "Vehicle state: ignition off.",
          "vehicle_state.ignition_on": "Vehicle state: ignition on.",
          "vehicle_state.indicator_left": "Vehicle state: indicator left.",
          "vehicle_state.indicator_right": "Vehicle state: indicator right.",
          "vehicle_state.reversing": "Vehicle state: reversing."
        }
      },
      "EventTypeFilter": {
        "enum": [
          "adas.bridge_detected",
          "adas.bridge_recognition",
          "adas.bridge_stop",
          "adas.following_distance_warning",
          "adas.forward_collision_warning",
          "adas.human_detection",
          "adas.human_detection_error",
          "adas.lane_departure",
          "diagnostics.camera_covered",
          "diagnostics.general",
          "diagnostics.gps_fault",
          "diagnostics.storage_abnormal",
          "diagnostics.system",
          "diagnostics.video_loss",
          "driver_behaviour.harsh_acceleration",
          "driver_behaviour.harsh_braking",
          "driver_behaviour.harsh_cornering",
          "driver_behaviour.shock",
          "driver_behaviour.speeding",
          "dsm.distraction",
          "dsm.fatigue",
          "dsm.mobile_phone",
          "dsm.no_driver",
          "dsm.smoking",
          "requested_data.video",
          "safety.emergency_999",
          "safety.external_alarms_off",
          "safety.panic_button",
          "safety.thumbs_up",
          "vehicle_state.ignition_off",
          "vehicle_state.ignition_on",
          "vehicle_state.indicator_left",
          "vehicle_state.indicator_right",
          "vehicle_state.reversing",
          "adas.*",
          "diagnostics.*",
          "driver_behaviour.*",
          "dsm.*",
          "requested_data.*",
          "safety.*",
          "vehicle_state.*"
        ],
        "type": "string",
        "description": "A device event type, or `family.*` for every event type of a family. Used in `eventTypeFilters`; it narrows `spillard.event.raised.v1` only.",
        "x-enumDescriptions": {
          "adas.bridge_detected": "Driver assistance (ADAS): bridge detected.",
          "adas.bridge_recognition": "Driver assistance (ADAS): bridge recognition.",
          "adas.bridge_stop": "Driver assistance (ADAS): bridge stop.",
          "adas.following_distance_warning": "Driver assistance (ADAS): following distance warning.",
          "adas.forward_collision_warning": "Driver assistance (ADAS): forward collision warning.",
          "adas.human_detection": "Driver assistance (ADAS): human detection.",
          "adas.human_detection_error": "Driver assistance (ADAS): human detection error.",
          "adas.lane_departure": "Driver assistance (ADAS): lane departure.",
          "diagnostics.camera_covered": "Device diagnostics: camera covered.",
          "diagnostics.general": "Device diagnostics: general.",
          "diagnostics.gps_fault": "Device diagnostics: gps fault.",
          "diagnostics.storage_abnormal": "Device diagnostics: storage abnormal.",
          "diagnostics.system": "Device diagnostics: system.",
          "diagnostics.video_loss": "Device diagnostics: video loss.",
          "driver_behaviour.harsh_acceleration": "Driver behavior: harsh acceleration.",
          "driver_behaviour.harsh_braking": "Driver behavior: harsh braking.",
          "driver_behaviour.harsh_cornering": "Driver behavior: harsh cornering.",
          "driver_behaviour.shock": "Driver behavior: shock.",
          "driver_behaviour.speeding": "Driver behavior: speeding.",
          "dsm.distraction": "Driver state monitoring: distraction.",
          "dsm.fatigue": "Driver state monitoring: fatigue.",
          "dsm.mobile_phone": "Driver state monitoring: mobile phone.",
          "dsm.no_driver": "Driver state monitoring: no driver.",
          "dsm.smoking": "Driver state monitoring: smoking.",
          "requested_data.video": "Video request: the event Spillard records for a video request.",
          "safety.emergency_999": "Safety: emergency 999.",
          "safety.external_alarms_off": "Safety: external alarms off.",
          "safety.panic_button": "Safety: panic button.",
          "safety.thumbs_up": "Safety: thumbs up.",
          "vehicle_state.ignition_off": "Vehicle state: ignition off.",
          "vehicle_state.ignition_on": "Vehicle state: ignition on.",
          "vehicle_state.indicator_left": "Vehicle state: indicator left.",
          "vehicle_state.indicator_right": "Vehicle state: indicator right.",
          "vehicle_state.reversing": "Vehicle state: reversing.",
          "adas.*": "Every driver assistance (adas) event type.",
          "diagnostics.*": "Every device diagnostics event type.",
          "driver_behaviour.*": "Every driver behavior event type.",
          "dsm.*": "Every driver state monitoring event type.",
          "requested_data.*": "Every video request event type.",
          "safety.*": "Every safety event type.",
          "vehicle_state.*": "Every vehicle state event type."
        }
      },
      "EventVehicleReference": {
        "required": [
          "id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The vehicle's id (UUID). `GET /v1/vehicles/{vehicleId}` reads it.",
            "format": "uuid"
          },
          "registrationNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's registration number (number plate) when the event was raised. Null when it was not set. Webhook messages call it `registration`."
          }
        },
        "description": "The vehicle of an event: its id and the registration number stored with the event. Other vehicle fields, such as\nthe chassis number, are on the vehicle itself (`GET /v1/vehicles/{vehicleId}`)."
      },
      "EventView": {
        "enum": [
          "full",
          "basic"
        ],
        "type": "string",
        "description": "How much of each event an event list returns.",
        "x-enumDescriptions": {
          "full": "Every field (the default).",
          "basic": "Without the per-sample arrays: `gSensorFrames`, `gyroscopeFrames`, `speedPointsKph` and `headingPointsDegrees` are null. For a cheap synchronization of many events."
        }
      },
      "Fleet": {
        "required": [
          "id",
          "name",
          "organization",
          "deviceCount",
          "assignedDeviceCount",
          "unassignedDeviceCount"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The fleet's id (UUID). `GET /v1/fleets/{fleetId}` reads it.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The fleet's display name."
          },
          "organization": {
            "description": "The organization that owns the fleet, with its name.",
            "$ref": "#/components/schemas/OrganizationReference"
          },
          "deviceCount": {
            "type": "integer",
            "description": "The number of devices in the fleet.",
            "format": "int32"
          },
          "assignedDeviceCount": {
            "type": "integer",
            "description": "The number of devices in the fleet that are fitted to a vehicle.",
            "format": "int32"
          },
          "unassignedDeviceCount": {
            "type": "integer",
            "description": "The number of devices in the fleet that are not fitted to a vehicle. It equals `deviceCount` minus `assignedDeviceCount`.",
            "format": "int32"
          }
        },
        "description": "A fleet: a group of vehicles, drivers and devices within an organization."
      },
      "FleetPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Fleet"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of fleets."
      },
      "FuelType": {
        "enum": [
          "diesel",
          "electric",
          "hybrid",
          "lpg",
          "petrol",
          "other"
        ],
        "type": "string",
        "description": "The fuel type of a vehicle.",
        "x-enumDescriptions": {
          "diesel": "Diesel.",
          "electric": "Electric.",
          "hybrid": "Hybrid.",
          "lpg": "Liquefied petroleum gas.",
          "petrol": "Petrol.",
          "other": "Another fuel type, or not recorded."
        }
      },
      "GeoPoint": {
        "required": [
          "latitude",
          "longitude"
        ],
        "type": "object",
        "properties": {
          "latitude": {
            "type": "number",
            "description": "Latitude in decimal degrees, from -90 to 90.",
            "format": "double"
          },
          "longitude": {
            "type": "number",
            "description": "Longitude in decimal degrees, from -180 to 180.",
            "format": "double"
          }
        },
        "description": "A WGS84 position in decimal degrees."
      },
      "GpsCoverage": {
        "enum": [
          "full",
          "partial",
          "none"
        ],
        "type": "string",
        "description": "Whether a journey had a GPS signal.",
        "x-enumDescriptions": {
          "full": "A GPS signal throughout the journey.",
          "partial": "A GPS signal in parts of the journey only.",
          "none": "No GPS signal at all: positions and distance are unknown."
        }
      },
      "GSensorFrame": {
        "required": [
          "xAxis",
          "yAxis",
          "zAxis",
          "sequenceNumber",
          "sampledAt"
        ],
        "type": "object",
        "properties": {
          "xAxis": {
            "type": "number",
            "description": "The lateral (X) reading, in the device's raw g-sensor units.",
            "format": "double"
          },
          "yAxis": {
            "type": "number",
            "description": "The longitudinal (Y) reading, in the device's raw g-sensor units.",
            "format": "double"
          },
          "zAxis": {
            "type": "number",
            "description": "The vertical (Z) reading, in the device's raw g-sensor units.",
            "format": "double"
          },
          "sequenceNumber": {
            "type": "integer",
            "description": "The sample's position in the event's accelerometer burst. It starts at 0 and ascends.",
            "format": "int32"
          },
          "sampledAt": {
            "type": "string",
            "description": "When the sample was taken (UTC, ISO 8601).",
            "format": "date-time"
          }
        },
        "description": "One sample of the device's accelerometer: the acceleration along each axis."
      },
      "GyroscopeFrame": {
        "required": [
          "xAxis",
          "yAxis",
          "zAxis",
          "sequenceNumber",
          "sampledAt"
        ],
        "type": "object",
        "properties": {
          "xAxis": {
            "type": "number",
            "description": "The rotation around the lateral (X) axis, in the device's raw gyroscope units.",
            "format": "double"
          },
          "yAxis": {
            "type": "number",
            "description": "The rotation around the longitudinal (Y) axis, in the device's raw gyroscope units.",
            "format": "double"
          },
          "zAxis": {
            "type": "number",
            "description": "The rotation around the vertical (Z) axis, in the device's raw gyroscope units.",
            "format": "double"
          },
          "sequenceNumber": {
            "type": "integer",
            "description": "The sample's position in the event's gyroscope burst. It starts at 0 and ascends.",
            "format": "int32"
          },
          "sampledAt": {
            "type": "string",
            "description": "When the sample was taken (UTC, ISO 8601).",
            "format": "date-time"
          }
        },
        "description": "One sample of the device's gyroscope: the rotation around each axis."
      },
      "HumanDetectionOperationalTime": {
        "required": [
          "date",
          "organization",
          "fleet",
          "vehicle",
          "operationalSeconds",
          "highCount",
          "mediumCount",
          "lowCount"
        ],
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "description": "The day (UTC), as `YYYY-MM-DD`.",
            "format": "date"
          },
          "organization": {
            "description": "The organization the vehicle belonged to on that day.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "description": "The fleet the vehicle belonged to on that day.",
            "$ref": "#/components/schemas/IdReference"
          },
          "vehicle": {
            "description": "The vehicle.",
            "$ref": "#/components/schemas/IdReference"
          },
          "operationalSeconds": {
            "type": "integer",
            "description": "How long the vehicle's journeys ran on that day, in seconds. A journey that crosses midnight counts on each day it covers.",
            "format": "int64"
          },
          "highCount": {
            "type": "integer",
            "description": "The number of human detection events of high severity (red in the Spillard web app) on that day.",
            "format": "int64"
          },
          "mediumCount": {
            "type": "integer",
            "description": "The number of human detection events of medium severity (amber in the Spillard web app) on that day.",
            "format": "int64"
          },
          "lowCount": {
            "type": "integer",
            "description": "The number of human detection events of low severity (green in the Spillard web app) on that day.",
            "format": "int64"
          }
        },
        "description": "The human detection figures of one vehicle on one day (UTC): how long the vehicle was driving and how many classified\nhuman detection events (ADAS) it raised. A vehicle appears on a day only when it has at least one classified human\ndetection event that day."
      },
      "HumanDetectionOperationalTimePage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HumanDetectionOperationalTime"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of human-detection figures, one per vehicle and day."
      },
      "IdReference": {
        "required": [
          "id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The referenced resource's id (UUID). Use it in the matching `/v1` read.",
            "format": "uuid"
          }
        },
        "description": "A reference to another resource by its id (a UUID). A missing resource is null in place of the whole reference."
      },
      "Journey": {
        "required": [
          "id",
          "idleSeconds",
          "organization",
          "processedAt",
          "revision",
          "speedingCount",
          "gpsCoverage"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The journey's id. Use it as `{journeyId}` in `/v1/journeys` paths. Journey webhooks carry it as `journeyId`."
          },
          "startedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the journey began (UTC, ISO 8601). Null when the journey has no start point.",
            "format": "date-time"
          },
          "endedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the journey ended (UTC, ISO 8601). Null when the journey has no end point, for example while it is in progress.",
            "format": "date-time"
          },
          "start": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Place"
              }
            ],
            "description": "Where the journey began. Null when the journey has no start point."
          },
          "end": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Place"
              }
            ],
            "description": "Where the journey ended. Null when the journey has no end point."
          },
          "distanceMeters": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The distance traveled in meters. Null when no length was computed.",
            "format": "int32"
          },
          "durationSeconds": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The journey's duration in seconds. Null when the start or the end is missing.",
            "format": "int32"
          },
          "idleSeconds": {
            "type": "integer",
            "description": "The time spent idling in seconds.",
            "format": "int32"
          },
          "maxSpeedKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The highest speed reached in km/h. Null when not recorded.",
            "format": "int32"
          },
          "organization": {
            "description": "The organization that owns the journey.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet of the journey's vehicle. Null when the vehicle has no fleet."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle that made the journey. Null when it cannot be resolved."
          },
          "device": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DeviceReference"
              }
            ],
            "description": "The device that recorded the journey. Null when it cannot be resolved."
          },
          "frameCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The number of positions (frames) available for the journey. Null when the journey has no route.",
            "format": "int32"
          },
          "processedAt": {
            "type": "string",
            "description": "When the journey was last processed or reprocessed (UTC, ISO 8601).",
            "format": "date-time"
          },
          "revision": {
            "type": "integer",
            "description": "The journey's revision. It is 1 when the journey is first stored and rises each time the stored journey changes. It is the same value as in the `spillard.journey.completed.v1` webhook. A reprocessed journey gets a new id and starts again at 1.",
            "format": "int32"
          },
          "speedingCount": {
            "type": "integer",
            "description": "The number of speeding episodes in the journey. 0 when there were none.",
            "format": "int32"
          },
          "gpsCoverage": {
            "type": "string",
            "description": "The GPS signal during the journey: `full` throughout, `partial` in parts, or `none`. Known values: `full`, `partial`, `none`. More values can be added; handle one you do not know."
          }
        },
        "description": "A journey with its start, end and totals. Its positions are at `GET /v1/journeys/{journeyId}/frames`.\nJourneys carry no driver: Spillard does not know who drove a journey."
      },
      "JourneyCompletedData": {
        "required": [
          "journeyId",
          "revision",
          "replaces",
          "startedAt",
          "endedAt",
          "start",
          "end",
          "durationSeconds",
          "speedingCount",
          "gpsCoverage",
          "organization",
          "device"
        ],
        "type": "object",
        "properties": {
          "journeyId": {
            "type": "string",
            "description": "The journey's id (a string, not a UUID): `GET /v1/journeys/{journeyId}` reads it."
          },
          "revision": {
            "type": "integer",
            "description": "The revision of the journey, starting at 1; a reprocessed journey comes again with a higher revision.",
            "format": "int32"
          },
          "replaces": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The ids of the journeys this one replaces: delete them. An empty list, never null."
          },
          "startedAt": {
            "type": "string",
            "description": "When the journey started (UTC, ISO 8601).",
            "format": "date-time"
          },
          "endedAt": {
            "type": "string",
            "description": "When the journey ended (UTC, ISO 8601).",
            "format": "date-time"
          },
          "start": {
            "description": "Where the journey started.",
            "$ref": "#/components/schemas/WebhookPlace"
          },
          "end": {
            "description": "Where the journey ended.",
            "$ref": "#/components/schemas/WebhookPlace"
          },
          "distanceMeters": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The distance driven, in meters; null when unknown.",
            "format": "int32"
          },
          "durationSeconds": {
            "type": "integer",
            "description": "The time from `startedAt` to `endedAt`, in seconds.",
            "format": "int32"
          },
          "movingSeconds": {
            "type": [
              "null",
              "integer"
            ],
            "description": "How long the vehicle moved, in seconds; null when unknown.",
            "format": "int32"
          },
          "idleSeconds": {
            "type": [
              "null",
              "integer"
            ],
            "description": "How long the vehicle stood still with the ignition on, in seconds; null when unknown.",
            "format": "int32"
          },
          "ignitionOnSeconds": {
            "type": [
              "null",
              "integer"
            ],
            "description": "How long the ignition was on, in seconds; null when unknown.",
            "format": "int32"
          },
          "maxSpeedKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The highest speed of the journey in km/h; null when unknown.",
            "format": "int32"
          },
          "speedingCount": {
            "type": "integer",
            "description": "How many times the vehicle went over the speed limit.",
            "format": "int32"
          },
          "gpsCoverage": {
            "type": "string",
            "description": "Whether the journey had a GPS signal throughout, in parts or not at all. Known values: `full`, `partial`, `none`. More values can be added; handle one you do not know."
          },
          "organization": {
            "description": "The organization the journey belongs to (also when the message reaches an endpoint of an organization above it).",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet of the vehicle, or null when it has none."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/WebhookVehicleReference"
              }
            ],
            "description": "The vehicle the device is fitted to, or null when it is not fitted to one."
          },
          "device": {
            "description": "The device that recorded the journey.",
            "$ref": "#/components/schemas/DeviceReference"
          }
        },
        "description": "The `data` of `spillard.journey.completed.v1`: a completed or reprocessed journey. The envelope's\n`subject` is `journeys/{journeyId}` and its `time` is `endedAt`. Keep the highest `revision`\nand delete the journeys listed in `replaces`. There is no `driver` in v1: Spillard does not know who drove\na journey; a driver can be added later as a new field."
      },
      "JourneyCompletedMessage": {
        "required": [
          "id",
          "type",
          "source",
          "time",
          "data",
          "specversion",
          "datacontenttype"
        ],
        "type": "object",
        "properties": {
          "specversion": {
            "const": "1.0",
            "type": "string",
            "description": "The CloudEvents specification version, always `1.0`."
          },
          "id": {
            "type": "string",
            "description": "The message id, equal to the `webhook-id` header. A retry keeps the id: deduplicate on it. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "type": {
            "const": "spillard.journey.completed.v1",
            "type": "string",
            "description": "The webhook type, always `spillard.journey.completed.v1` for this message."
          },
          "source": {
            "type": "string",
            "description": "The organization the fact belongs to: `/organizations/{organizationId}`."
          },
          "subject": {
            "type": [
              "null",
              "string"
            ],
            "description": "The entity the fact is about (for example `events/{eventId}`); absent from the `telemetry.live` and `telemetry.history` batches."
          },
          "time": {
            "type": "string",
            "description": "When the fact occurred (UTC, milliseconds).",
            "format": "date-time"
          },
          "datacontenttype": {
            "const": "application/json",
            "type": "string",
            "description": "The media type of `data`, always `application/json`."
          },
          "data": {
            "description": "The payload of the message.",
            "$ref": "#/components/schemas/JourneyCompletedData"
          }
        },
        "description": "The `spillard.journey.completed.v1` webhook message: a CloudEvents 1.0 envelope in structured mode whose `data` carries the payload. Within v1 the envelope and payload only gain fields: tolerate unknown fields."
      },
      "JourneyPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JourneySummary"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of journeys."
      },
      "JourneySummary": {
        "required": [
          "id",
          "startedAt",
          "durationSeconds",
          "organization"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The journey's id. Use it as `{journeyId}` in `/v1/journeys` paths. Journey webhooks carry it as `journeyId`."
          },
          "startedAt": {
            "type": "string",
            "description": "When the journey began (UTC, ISO 8601). Always present in a list.",
            "format": "date-time"
          },
          "endedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the journey ended (UTC, ISO 8601). Null while the journey is in progress.",
            "format": "date-time"
          },
          "start": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Place"
              }
            ],
            "description": "Where the journey began. Null when unknown. A list carries only the address label."
          },
          "end": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Place"
              }
            ],
            "description": "Where the journey ended. Null when unknown. A list carries only the address label."
          },
          "distanceMeters": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The distance traveled in meters. Null when unknown.",
            "format": "int32"
          },
          "idleSeconds": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The time spent idling in seconds. Null when unknown.",
            "format": "int32"
          },
          "durationSeconds": {
            "type": "integer",
            "description": "The journey's duration in seconds.",
            "format": "int32"
          },
          "maxSpeedKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The highest speed reached in km/h. Null when unknown.",
            "format": "int32"
          },
          "organization": {
            "description": "The organization that owns the journey.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet of the journey's vehicle. Null when the vehicle has no fleet."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle that made the journey. Null when it cannot be resolved."
          },
          "device": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DeviceReference"
              }
            ],
            "description": "The device that recorded the journey. Null when it cannot be resolved."
          }
        },
        "description": "A journey as lists show it. Journeys carry no driver: Spillard does not know who drove a journey."
      },
      "MediaItem": {
        "required": [
          "id",
          "uri",
          "sequenceNumber"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The id of the file. It is the same on every read. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "uri": {
            "type": "string",
            "description": "The signed download link for the file. It is short-lived; read the files again for a fresh link.",
            "format": "uri"
          },
          "expiresAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the download link stops working (UTC, ISO 8601). By default 60 minutes after the read, or the `linkTtlSeconds` the read asked for (60 to 86,400 seconds). Null when the link does not expire.",
            "format": "date-time"
          },
          "mimeType": {
            "type": [
              "null",
              "string"
            ],
            "description": "The media type of the file, for example `video/mp4` or `image/jpeg`. Null when the device did not report one."
          },
          "purpose": {
            "type": [
              "null",
              "string"
            ],
            "description": "What the file is: a `video` clip, or the `thumbnail` (preview image) of a clip of the same media request and channel. Null for any other kind of file. Known values: `video`, `snapshot`, `thumbnail`. More values can be added; handle one you do not know."
          },
          "receivedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When Spillard received the file from the device (UTC, ISO 8601). Null when unknown.",
            "format": "date-time"
          },
          "channel": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Channel"
              }
            ],
            "description": "The camera channel the file came from, with its number and label. Null when the file is not tied to a channel."
          },
          "sequenceNumber": {
            "type": "integer",
            "description": "The file's position among the files of its event, in the order the device sent them. It ascends.",
            "format": "int32"
          },
          "fileName": {
            "type": [
              "null",
              "string"
            ],
            "description": "The file name the device gave. Null when unknown."
          },
          "fileExtension": {
            "type": [
              "null",
              "string"
            ],
            "description": "The file extension without the dot, for example `mp4` or `jpg`. It is the stored extension, or else the one in `fileName`. Null when unknown."
          },
          "event": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/EventReference"
              }
            ],
            "description": "The event the file belongs to. Null when the file is not linked to an event."
          },
          "command": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CommandReference"
              }
            ],
            "description": "The media request that produced the file. Its id is the `mediaRequestId` of `GET /v1/media-requests/{mediaRequestId}`. Null when the file was not produced by a request."
          }
        },
        "description": "A media file (a video clip or its thumbnail) with a freshly signed download link. Do not store the link; read the files again for a fresh one."
      },
      "MediaList": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MediaItem"
            },
            "description": "The media files. The list is empty, not an error, when the resource exists but has no files yet."
          }
        },
        "description": "The media files of an event or media request, each with a download link. The links are issued fresh on every read."
      },
      "MediaPurpose": {
        "enum": [
          "video",
          "snapshot",
          "thumbnail"
        ],
        "type": "string",
        "description": "What a media file is: a video clip, a still image, or the preview of a clip.",
        "x-enumDescriptions": {
          "video": "A video clip.",
          "snapshot": "A still image that is not the preview of a clip.",
          "thumbnail": "The preview image of a video clip of the same request and camera channel."
        }
      },
      "MediaRequest": {
        "required": [
          "id",
          "state",
          "mediaReady"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The id of the media request. It is the `mediaRequestId` of the webhook message. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "state": {
            "type": "string",
            "description": "The state of the request. The Spillard web app shows `dispatched` as Sent, `acknowledged` as Received, `completed` as Done and `timed_out` as Inconclusive. Poll until it is final. Known values: `queued`, `dispatched`, `acknowledged`, `completed`, `cancelled`, `failed`, `timed_out`. In progress: `queued`, `dispatched`, `acknowledged`. Final: `completed`, `cancelled`, `failed`, `timed_out`. The final states do not change within v1, so poll until `state` is one of them. More in-progress states can be added; treat a state you do not know as in progress."
          },
          "mediaReady": {
            "type": "boolean",
            "description": "True when at least one file of the request can be read from `GET /v1/media-requests/{mediaRequestId}/media`."
          },
          "failureReason": {
            "type": [
              "null",
              "string"
            ],
            "description": "The failure text the device gave for a `failed` or `timed_out` request, at most 256 characters. Null otherwise, or when the device gave none."
          },
          "externalId": {
            "type": [
              "null",
              "string"
            ],
            "description": "Your own reference, exactly as you sent it when you created the request. Null when you gave none."
          }
        },
        "description": "A media request (a video request) and its `state`: `queued`, `dispatched` or `acknowledged` while it\nruns; `completed`, `failed`, `cancelled` or `timed_out` at the end."
      },
      "MediaRequestAccepted": {
        "required": [
          "id",
          "links",
          "device",
          "event"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The id of the media request. It is the `mediaRequestId` of the webhook message and the command id of `GET /v1/commands/{commandId}`. Poll it with `GET /v1/media-requests/{mediaRequestId}`. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "links": {
            "description": "The links of the request.",
            "$ref": "#/components/schemas/MediaRequestLinks"
          },
          "device": {
            "description": "The device the request was sent to.",
            "$ref": "#/components/schemas/DeviceReference"
          },
          "event": {
            "description": "The event the request belongs to: the event you named, or, for a request made with `POST /v1/media-requests`, the `requested_data.video` event Spillard created for it. `GET /v1/events/{eventId}` reads it.",
            "$ref": "#/components/schemas/EventReference"
          }
        },
        "description": "An accepted media request (a video request). It carries no download link. Poll `links.self` until `state` is\nfinal. You can also wait for the `spillard.media.request.completed.v1` webhook, but do not rely on it alone: only its\n`completed` outcome is reliable today (`failed` and `no_data` are in future development), and a request\nthat ends as `timed_out` may send no message."
      },
      "MediaRequestCompletedData": {
        "required": [
          "mediaRequestId",
          "trigger",
          "outcome",
          "requestedAt",
          "completedAt",
          "request",
          "organization",
          "device",
          "links"
        ],
        "type": "object",
        "properties": {
          "mediaRequestId": {
            "type": "string",
            "description": "The id of the media request: the `id` of `GET /v1/media-requests/{mediaRequestId}`. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "trigger": {
            "type": "string",
            "description": "Who made the request: a person or an API client (`manual`), or Spillard for an event (`event`). Known values: `manual`, `event`. More values can be added; handle one you do not know."
          },
          "externalId": {
            "type": [
              "null",
              "string"
            ],
            "description": "Your `externalId` from the request, exactly as sent; null when none was given."
          },
          "outcome": {
            "type": "string",
            "description": "How the request ended. Only `completed` is reliable today; `failed` and `no_data` are in future development, do not rely on them. Known values: `completed`, `failed`, `no_data`. Every outcome is final for the request, except that `failed` can later be corrected to `completed`. The outcomes do not change within v1."
          },
          "requestedAt": {
            "type": "string",
            "description": "When the request was made (UTC, ISO 8601).",
            "format": "date-time"
          },
          "completedAt": {
            "type": "string",
            "description": "When the request reached this outcome (UTC, ISO 8601). It is also the envelope's `time`. When two messages arrive for one request, keep the one with the later `completedAt`.",
            "format": "date-time"
          },
          "request": {
            "description": "What was requested: the start and length of the clip, its channels and the overlay.",
            "$ref": "#/components/schemas/MediaRequestDetails"
          },
          "eventId": {
            "type": [
              "null",
              "string"
            ],
            "description": "The event the request belongs to: the event you named, or the event Spillard created for a request made with `POST /v1/media-requests`. `GET /v1/events/{eventId}` reads it. Null when the request has no event. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "organization": {
            "description": "The organization the request belongs to (also when the message reaches an endpoint of an organization above it).",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet of the device's vehicle, or null when it has none."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/WebhookVehicleReference"
              }
            ],
            "description": "The vehicle the device is fitted to, or null when it is not fitted to one. Its `registration` is always null when `outcome` is `failed` or `no_data`."
          },
          "driver": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle's driver, or null when none is known."
          },
          "device": {
            "description": "The device the video was requested from.",
            "$ref": "#/components/schemas/DeviceReference"
          },
          "links": {
            "description": "Where to get the files and read the event in the Connect API.",
            "$ref": "#/components/schemas/MediaRequestCompletedLinks"
          }
        },
        "description": "The `data` of `spillard.media.request.completed.v1`: the outcome of a media request (a video request): `completed`,\n`failed` or `no_data`. Only `completed` is reliable today; `failed` and `no_data` are in future\ndevelopment, do not rely on them. Poll `GET /v1/media-requests/{mediaRequestId}` until its `state` is final: a\nrequest that ends as `timed_out` may send no message. The envelope's `subject` is\n`mediaRequests/{mediaRequestId}` and its `time` is `completedAt`. A `failed` outcome can later be\ncorrected to `completed`: keep the message with the later `completedAt`."
      },
      "MediaRequestCompletedLinks": {
        "required": [
          "media"
        ],
        "type": "object",
        "properties": {
          "media": {
            "type": "string",
            "description": "The request's files: `/v1/media-requests/{mediaRequestId}/media`. It is present for every outcome; for `failed` and `no_data` it lists whatever has arrived, possibly nothing.",
            "format": "uri-reference"
          },
          "event": {
            "type": [
              "null",
              "string"
            ],
            "description": "The event: `/v1/events/{eventId}`; null (the field is still present) when `eventId` is null.",
            "format": "uri-reference"
          }
        },
        "description": "Connect API links of a media request's outcome, as paths on the API host (for example `https://connect.spillard.live`)."
      },
      "MediaRequestCompletedMessage": {
        "required": [
          "id",
          "type",
          "source",
          "time",
          "data",
          "specversion",
          "datacontenttype"
        ],
        "type": "object",
        "properties": {
          "specversion": {
            "const": "1.0",
            "type": "string",
            "description": "The CloudEvents specification version, always `1.0`."
          },
          "id": {
            "type": "string",
            "description": "The message id, equal to the `webhook-id` header. A retry keeps the id: deduplicate on it. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "type": {
            "const": "spillard.media.request.completed.v1",
            "type": "string",
            "description": "The webhook type, always `spillard.media.request.completed.v1` for this message."
          },
          "source": {
            "type": "string",
            "description": "The organization the fact belongs to: `/organizations/{organizationId}`."
          },
          "subject": {
            "type": [
              "null",
              "string"
            ],
            "description": "The entity the fact is about (for example `events/{eventId}`); absent from the `telemetry.live` and `telemetry.history` batches."
          },
          "time": {
            "type": "string",
            "description": "When the fact occurred (UTC, milliseconds).",
            "format": "date-time"
          },
          "datacontenttype": {
            "const": "application/json",
            "type": "string",
            "description": "The media type of `data`, always `application/json`."
          },
          "data": {
            "description": "The payload of the message.",
            "$ref": "#/components/schemas/MediaRequestCompletedData"
          }
        },
        "description": "The `spillard.media.request.completed.v1` webhook message: a CloudEvents 1.0 envelope in structured mode whose `data` carries the payload. Within v1 the envelope and payload only gain fields: tolerate unknown fields."
      },
      "MediaRequestDetails": {
        "required": [
          "startAt",
          "durationSeconds",
          "channels",
          "overlay"
        ],
        "type": "object",
        "properties": {
          "startAt": {
            "type": "string",
            "description": "The start of the requested clip (UTC, ISO 8601): the `startAt` you sent, or the start worked out from `midpointAt` or from the event.",
            "format": "date-time"
          },
          "durationSeconds": {
            "type": "integer",
            "description": "The length of the requested clip in seconds.",
            "format": "int32"
          },
          "channels": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int32"
            },
            "description": "The camera channel numbers that were requested, from 1 (for example `[1, 2]`)."
          },
          "overlay": {
            "type": "boolean",
            "description": "Whether the on-screen telemetry overlay was requested for the video."
          }
        },
        "description": "What a media request asked for: the start of the clip, its length, the channels and the overlay flag."
      },
      "MediaRequestLinks": {
        "required": [
          "self"
        ],
        "type": "object",
        "properties": {
          "self": {
            "type": "string",
            "description": "The relative URL of the media request: `GET /v1/media-requests/{mediaRequestId}`.",
            "format": "uri-reference"
          }
        },
        "description": "Links of an accepted media request."
      },
      "MediaRequestOutcome": {
        "enum": [
          "completed",
          "failed",
          "no_data"
        ],
        "type": "string",
        "description": "How a media request ended. Every outcome is final for the request, except that `failed` can later be corrected to `completed`. The outcomes do not change within v1.",
        "x-enumDescriptions": {
          "completed": "Every part of the recording arrived. Only this outcome is reliable today.",
          "failed": "The request failed or was cancelled. In future development: do not rely on it. It can later be corrected to `completed` when the recording arrives after all.",
          "no_data": "The request failed because the device has no recording for the requested time. In future development: do not rely on it."
        }
      },
      "MediaRequestTrigger": {
        "enum": [
          "manual",
          "event"
        ],
        "type": "string",
        "description": "What created a media request.",
        "x-enumDescriptions": {
          "manual": "A person or an API client made the request.",
          "event": "Spillard requested the video of an event automatically."
        }
      },
      "MediaStatus": {
        "enum": [
          "none",
          "pending",
          "available"
        ],
        "type": "string",
        "description": "Where an event's media stands.",
        "x-enumDescriptions": {
          "none": "No media, and no media request for the event is open.",
          "pending": "No media yet; a video request for the event is queued, sent or received by the device.",
          "available": "Media arrived (`hasMedia` is true)."
        }
      },
      "MediaType": {
        "enum": [
          "video"
        ],
        "type": "string",
        "description": "The kind of media a media request captures: only `video`.",
        "x-enumDescriptions": {
          "video": "A video clip."
        }
      },
      "MoveDeviceFleetRequest": {
        "required": [
          "fleetId"
        ],
        "type": "object",
        "properties": {
          "fleetId": {
            "type": "string",
            "description": "The id of the target fleet. It must be a fleet you can see in the device's organization: your client reads the organization with every fleet, or the fleet is one of the client's allowed fleets.",
            "format": "uuid"
          }
        },
        "description": "The fleet to move a device to."
      },
      "MoveVehicleRequest": {
        "required": [
          "fleetId"
        ],
        "type": "object",
        "properties": {
          "fleetId": {
            "type": "string",
            "description": "The id of the target fleet. It must be a fleet you can see: your client reads its organization with every fleet, or the fleet is one of the client's allowed fleets. The fleet's organization becomes the vehicle's organization. It can be another organization of your access.",
            "format": "uuid"
          }
        },
        "description": "The fleet to move a vehicle to, with its driver and devices."
      },
      "Organization": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The organization's id (UUID).",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The organization's display name."
          },
          "timeZone": {
            "type": [
              "null",
              "string"
            ],
            "description": "The organization's time zone as an IANA id, for example `Europe/London`. Null when not set."
          },
          "supportEmail": {
            "type": [
              "null",
              "string"
            ],
            "description": "The organization's support email address. Null when not set."
          },
          "supportPhone": {
            "type": [
              "null",
              "string"
            ],
            "description": "The organization's support phone number. Null when not set."
          }
        },
        "description": "An organization: the owner of fleets, vehicles, drivers and devices."
      },
      "OrganizationPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Organization"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of organizations."
      },
      "OrganizationReference": {
        "required": [
          "id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The organization's id (UUID). `GET /v1/organizations/{organizationId}` reads it.",
            "format": "uuid"
          },
          "name": {
            "type": [
              "null",
              "string"
            ],
            "description": "The organization's display name. Null when it has none."
          }
        },
        "description": "An organization reference: the organization's id and its name."
      },
      "Place": {
        "type": "object",
        "properties": {
          "location": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/GeoPoint"
              }
            ],
            "description": "The place's coordinates. Null when unknown."
          },
          "address": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Address"
              }
            ],
            "description": "The place's address. Null when unknown. When only a formatted address is known, `label` carries it."
          }
        },
        "description": "A place: a position and its address, for example where a journey started or ended."
      },
      "ProblemDetails": {
        "required": [
          "type",
          "title",
          "status",
          "errorCode",
          "retryable"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "A link to the page about the HTTP status (`https://connect.spillard.live/errors/{status}`).",
            "format": "uri"
          },
          "title": {
            "type": "string",
            "description": "The HTTP status in words, for example `Not Found`."
          },
          "status": {
            "type": "integer",
            "description": "The HTTP status code.",
            "format": "int32"
          },
          "detail": {
            "type": [
              "null",
              "string"
            ],
            "description": "What went wrong with this request, in English. For people, not for code."
          },
          "instance": {
            "type": [
              "null",
              "string"
            ],
            "description": "The path of the request that failed."
          },
          "errorCode": {
            "type": "string",
            "description": "The error code in the form `resource.reason`, stable within v1. The ErrorCode schema lists every code with its meaning; codes can be added within v1, so handle a code you do not know by the HTTP status."
          },
          "requestId": {
            "type": [
              "null",
              "string"
            ],
            "description": "Reserved; null in v1. Quote `traceId` instead."
          },
          "traceId": {
            "type": [
              "null",
              "string"
            ],
            "description": "The id of this request in Spillard's logs. Quote it when you contact Spillard support."
          },
          "retryable": {
            "type": "boolean",
            "description": "True when the same request may succeed later: 429 and every 5xx except 501 (wait for `Retry-After` when present). False for every 4xx and for 501; after 409 `idempotency.in_progress`, wait and send the same request again with the same key."
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "description": "The invalid fields and parameters, each with its messages (only on 400 `validation.failed` and `validation.cursor_invalid`)."
          }
        },
        "description": "An error (RFC 9457 problem details). Branch on `errorCode`, never on `title` or `detail`; `retryable` says whether the same request may succeed later."
      },
      "Scope": {
        "enum": [
          "organizations.read",
          "fleets.read",
          "fleets.write",
          "vehicles.read",
          "vehicles.write",
          "drivers.read",
          "drivers.write",
          "devices.read",
          "devices.write",
          "events.read",
          "journeys.read",
          "commands.read",
          "media.read",
          "media.write",
          "webhooks.read",
          "webhooks.write",
          "webhook-settings.read",
          "webhook-settings.write",
          "api-clients.read",
          "api-clients.write"
        ],
        "type": "string",
        "description": "An OAuth2 scope of the API.",
        "x-enumDescriptions": {
          "organizations.read": "Read the organizations your client can access.",
          "fleets.read": "Read fleets.",
          "fleets.write": "Create fleets.",
          "vehicles.read": "Read vehicles and their latest positions.",
          "vehicles.write": "Create, update and delete vehicles; assign drivers and devices to vehicles.",
          "drivers.read": "Read drivers.",
          "drivers.write": "Create, update and delete drivers.",
          "devices.read": "Read devices, device positions and the device catalog (models, protocols).",
          "devices.write": "Turn data from a device on or off; move a device to another fleet.",
          "events.read": "Read device events, including per device and per journey.",
          "journeys.read": "Read journeys and their frames.",
          "commands.read": "Read commands sent to devices.",
          "media.read": "Read media requests and get download links for footage.",
          "media.write": "Request video clips from devices.",
          "webhooks.read": "Read webhook endpoints and their failed or retrying deliveries.",
          "webhooks.write": "Create, change, test and delete webhook endpoints; rotate signing secrets; replay deliveries.",
          "webhook-settings.read": "See whether webhook delivery is on for the organization.",
          "webhook-settings.write": "Turn webhook delivery on or off for the organization.",
          "api-clients.read": "Read the organization's API clients.",
          "api-clients.write": "Create, change and delete API clients and rotate their secrets (scopes limited to your own)."
        }
      },
      "ShareEventRequest": {
        "type": "object",
        "properties": {
          "expiresAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the link stops working (UTC, ISO 8601). It must be in the future and at most 30 days ahead; another value is 400 `validation.failed`. Default: 30 days ahead. For an organization limited to the last 30 days of data, the link stops when the event turns 30 days old if that comes first.",
            "format": "date-time"
          },
          "name": {
            "maxLength": 100,
            "minLength": 3,
            "type": [
              "null",
              "string"
            ],
            "description": "The share's name in the web app's list of shared events, 3 to 100 characters. Default: \"API share\"."
          },
          "description": {
            "maxLength": 1000,
            "type": [
              "null",
              "string"
            ],
            "description": "A description shown with the shared event, at most 1,000 characters. Null for none."
          }
        },
        "description": "How to share an event. Every field is optional."
      },
      "TelemetryFrame": {
        "required": [
          "location",
          "recordedAt"
        ],
        "type": "object",
        "properties": {
          "location": {
            "description": "The position (WGS84).",
            "$ref": "#/components/schemas/GeoPoint"
          },
          "speedKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The ground speed in km/h. Null when the frame has no speed.",
            "format": "int32"
          },
          "headingDegrees": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The heading in degrees clockwise from north, 0 to 359. 0 is a real heading, not \"missing\". Null when not available.",
            "format": "int32"
          },
          "recordedAt": {
            "type": "string",
            "description": "When the position was recorded (UTC, ISO 8601).",
            "format": "date-time"
          }
        },
        "description": "One position recorded during a journey."
      },
      "TelemetryFramePage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TelemetryFrame"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of journey positions."
      },
      "TelemetryHistoryData": {
        "required": [
          "tracks"
        ],
        "type": "object",
        "properties": {
          "tracks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TelemetryHistoryTrack"
            },
            "description": "The tracks of the batch, one per device and window."
          }
        },
        "description": "The `data` of `spillard.telemetry.history.v1`: a batch of recorded tracks, one per device and window. The\nenvelope has no `subject`; its `time` is the latest `windowEndAt` of the tracks."
      },
      "TelemetryHistoryFrame": {
        "required": [
          "recordedAt",
          "location",
          "speedKph",
          "headingDegrees",
          "gpsValid"
        ],
        "type": "object",
        "properties": {
          "recordedAt": {
            "type": "string",
            "description": "When the device recorded the frame (UTC, ISO 8601).",
            "format": "date-time"
          },
          "location": {
            "description": "Where the device was; check `gpsValid` before you use it.",
            "$ref": "#/components/schemas/GeoPoint"
          },
          "speedKph": {
            "type": "integer",
            "description": "Speed in km/h.",
            "format": "int32"
          },
          "headingDegrees": {
            "type": "integer",
            "description": "Heading in degrees (0 to 359, clockwise from north).",
            "format": "int32"
          },
          "gpsValid": {
            "type": "boolean",
            "description": "Whether `location` is a usable GPS fix (finite, inside WGS84, not 0/0)."
          },
          "ignitionOn": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "The device's ignition flag; on whenever the frame moves at 3 km/h or more. Null when unknown."
          }
        },
        "description": "One recorded frame. Frames carry no altitude."
      },
      "TelemetryHistoryMessage": {
        "required": [
          "id",
          "type",
          "source",
          "time",
          "data",
          "specversion",
          "datacontenttype"
        ],
        "type": "object",
        "properties": {
          "specversion": {
            "const": "1.0",
            "type": "string",
            "description": "The CloudEvents specification version, always `1.0`."
          },
          "id": {
            "type": "string",
            "description": "The message id, equal to the `webhook-id` header. A retry keeps the id: deduplicate on it. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "type": {
            "const": "spillard.telemetry.history.v1",
            "type": "string",
            "description": "The webhook type, always `spillard.telemetry.history.v1` for this message."
          },
          "source": {
            "type": "string",
            "description": "The organization the fact belongs to: `/organizations/{organizationId}`."
          },
          "subject": {
            "type": [
              "null",
              "string"
            ],
            "description": "The entity the fact is about (for example `events/{eventId}`); absent from the `telemetry.live` and `telemetry.history` batches."
          },
          "time": {
            "type": "string",
            "description": "When the fact occurred (UTC, milliseconds).",
            "format": "date-time"
          },
          "datacontenttype": {
            "const": "application/json",
            "type": "string",
            "description": "The media type of `data`, always `application/json`."
          },
          "data": {
            "description": "The payload of the message.",
            "$ref": "#/components/schemas/TelemetryHistoryData"
          }
        },
        "description": "The `spillard.telemetry.history.v1` webhook message: a CloudEvents 1.0 envelope in structured mode whose `data` carries the payload. Within v1 the envelope and payload only gain fields: tolerate unknown fields."
      },
      "TelemetryHistoryTrack": {
        "required": [
          "windowStartAt",
          "windowEndAt",
          "backfill",
          "frameCount",
          "frames",
          "organization",
          "device"
        ],
        "type": "object",
        "properties": {
          "windowStartAt": {
            "type": "string",
            "description": "The time of the first frame of the track (UTC, ISO 8601).",
            "format": "date-time"
          },
          "windowEndAt": {
            "type": "string",
            "description": "The time of the last frame of the track (UTC, ISO 8601).",
            "format": "date-time"
          },
          "backfill": {
            "type": "boolean",
            "description": "True when the frames are older than an hour when the message is published, because the device was offline."
          },
          "frameCount": {
            "type": "integer",
            "description": "How many frames `frames` holds.",
            "format": "int32"
          },
          "frames": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TelemetryHistoryFrame"
            },
            "description": "The recorded frames, oldest first."
          },
          "organization": {
            "description": "The organization the track belongs to (also when the message reaches an endpoint of an organization above it).",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet of the device's vehicle, or null when it has none."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/WebhookVehicleReference"
              }
            ],
            "description": "The vehicle the device is fitted to, or null when it is not fitted to one."
          },
          "device": {
            "description": "The device that recorded the track.",
            "$ref": "#/components/schemas/DeviceReference"
          }
        },
        "description": "The frames one device recorded within a window, oldest first. There is no `driver` in v1: Spillard does not\nknow who drove; a driver can be added later as a new field."
      },
      "TelemetryLiveData": {
        "required": [
          "positions"
        ],
        "type": "object",
        "properties": {
          "positions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TelemetryLivePosition"
            },
            "description": "The latest positions of the organization's vehicles."
          }
        },
        "description": "The `data` of `spillard.telemetry.live.v1`: a batch of the latest positions of an organization's\nvehicles. The envelope has no `subject`; its `time` is the latest `recordedAt` of the positions."
      },
      "TelemetryLiveMessage": {
        "required": [
          "id",
          "type",
          "source",
          "time",
          "data",
          "specversion",
          "datacontenttype"
        ],
        "type": "object",
        "properties": {
          "specversion": {
            "const": "1.0",
            "type": "string",
            "description": "The CloudEvents specification version, always `1.0`."
          },
          "id": {
            "type": "string",
            "description": "The message id, equal to the `webhook-id` header. A retry keeps the id: deduplicate on it. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "type": {
            "const": "spillard.telemetry.live.v1",
            "type": "string",
            "description": "The webhook type, always `spillard.telemetry.live.v1` for this message."
          },
          "source": {
            "type": "string",
            "description": "The organization the fact belongs to: `/organizations/{organizationId}`."
          },
          "subject": {
            "type": [
              "null",
              "string"
            ],
            "description": "The entity the fact is about (for example `events/{eventId}`); absent from the `telemetry.live` and `telemetry.history` batches."
          },
          "time": {
            "type": "string",
            "description": "When the fact occurred (UTC, milliseconds).",
            "format": "date-time"
          },
          "datacontenttype": {
            "const": "application/json",
            "type": "string",
            "description": "The media type of `data`, always `application/json`."
          },
          "data": {
            "description": "The payload of the message.",
            "$ref": "#/components/schemas/TelemetryLiveData"
          }
        },
        "description": "The `spillard.telemetry.live.v1` webhook message: a CloudEvents 1.0 envelope in structured mode whose `data` carries the payload. Within v1 the envelope and payload only gain fields: tolerate unknown fields."
      },
      "TelemetryLivePosition": {
        "required": [
          "recordedAt",
          "location",
          "organization",
          "device"
        ],
        "type": "object",
        "properties": {
          "recordedAt": {
            "type": "string",
            "description": "When the device recorded the position (UTC, ISO 8601).",
            "format": "date-time"
          },
          "location": {
            "description": "Where the vehicle was.",
            "$ref": "#/components/schemas/GeoPoint"
          },
          "speedKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "Speed in km/h; null when unknown.",
            "format": "int32"
          },
          "headingDegrees": {
            "type": [
              "null",
              "integer"
            ],
            "description": "Heading in degrees (0 to 359, clockwise from north); null when unknown.",
            "format": "int32"
          },
          "ignitionOn": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Whether the ignition is on; null when the device did not report it."
          },
          "organization": {
            "description": "The organization the position belongs to (also when the message reaches an endpoint of an organization above it).",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet of the vehicle, or null when it has none."
          },
          "vehicle": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/WebhookVehicleReference"
              }
            ],
            "description": "The vehicle the device is fitted to, or null when it is not fitted to one."
          },
          "driver": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle's driver, or null when none is known."
          },
          "device": {
            "description": "The device that reported the position.",
            "$ref": "#/components/schemas/DeviceReference"
          }
        },
        "description": "One live position of a vehicle. The latest `recordedAt` per vehicle wins."
      },
      "TimeWindowMeta": {
        "required": [
          "effectiveFrom",
          "effectiveTo"
        ],
        "type": "object",
        "properties": {
          "effectiveFrom": {
            "type": "string",
            "description": "Start of the trigger-time window the list used (UTC, ISO 8601). It is the default when the request gave none.",
            "format": "date-time"
          },
          "effectiveTo": {
            "type": "string",
            "description": "End of the trigger-time window the list used (UTC, ISO 8601). It is the default when the request gave none.",
            "format": "date-time"
          },
          "effectiveReceivedFrom": {
            "type": [
              "null",
              "string"
            ],
            "description": "Start of the received-time window (UTC, ISO 8601). Null when no received-time window was applied.",
            "format": "date-time"
          },
          "effectiveReceivedTo": {
            "type": [
              "null",
              "string"
            ],
            "description": "End of the received-time window (UTC, ISO 8601). Null when no received-time window was applied.",
            "format": "date-time"
          }
        },
        "description": "The time windows an event list used. The trigger-time window is the last 24 hours by default and spans at most 92\ndays. The received-time fields are set only when a received-time window was asked for."
      },
      "UpdateApiClientRequest": {
        "type": "object",
        "properties": {
          "name": {
            "maxLength": 200,
            "minLength": 1,
            "type": [
              "null",
              "string"
            ],
            "description": "The new name, 1 to 200 characters. Omit it to keep the current name."
          },
          "scopes": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/Scope"
            },
            "description": "The new full set of scopes. Omit it to keep the current scopes."
          }
        },
        "description": "Changes to an API client. Omitted fields stay unchanged."
      },
      "UpdateDriverRequest": {
        "type": "object",
        "properties": {
          "firstName": {
            "maxLength": 50,
            "minLength": 1,
            "type": [
              "null",
              "string"
            ],
            "description": "The new given name, 1 to 50 characters, not blank. Omit it to keep the current name."
          },
          "lastName": {
            "maxLength": 50,
            "minLength": 1,
            "type": [
              "null",
              "string"
            ],
            "description": "The new family name, 1 to 50 characters, not blank. Omit it to keep the current name."
          },
          "phoneNumber": {
            "maxLength": 50,
            "minLength": 1,
            "type": [
              "null",
              "string"
            ],
            "description": "The new contact phone number, 1 to 50 characters, not blank. Omit it to keep the current number."
          }
        },
        "description": "Changes to a driver. Omitted fields stay unchanged."
      },
      "UpdateVehicleRequest": {
        "type": "object",
        "properties": {
          "registrationNumber": {
            "maxLength": 50,
            "minLength": 1,
            "type": [
              "null",
              "string"
            ],
            "description": "The new registration number (number plate), 1 to 50 characters, not blank. Omit it to keep the current value."
          },
          "make": {
            "maxLength": 50,
            "minLength": 1,
            "type": [
              "null",
              "string"
            ],
            "description": "The new make, 1 to 50 characters, not blank. Omit it to keep the current value."
          },
          "model": {
            "maxLength": 50,
            "minLength": 1,
            "type": [
              "null",
              "string"
            ],
            "description": "The new model name, 1 to 50 characters, not blank. Omit it to keep the current value."
          },
          "fuelType": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/FuelType"
              }
            ],
            "description": "The new fuel type. Omit it to keep the current value. An unknown value is 400 `validation.failed`."
          },
          "engineSizeLiters": {
            "minimum": 0,
            "type": [
              "null",
              "number"
            ],
            "description": "The new engine size in liters, 0 or more. Omit it to keep the current value. A negative value is 400 `validation.failed`.",
            "format": "double"
          },
          "vehicleType": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/VehicleType"
              }
            ],
            "description": "The new vehicle type. Omit it to keep the current value. An unknown value is 400 `validation.failed`."
          },
          "chassisNumber": {
            "maxLength": 100,
            "type": [
              "null",
              "string"
            ],
            "description": "The new chassis number (VIN), at most 100 characters. Omit it to keep the current value."
          },
          "yearOfManufacture": {
            "minimum": 1950,
            "type": [
              "null",
              "integer"
            ],
            "description": "The year the vehicle was made, from 1950 to the current year. Omit it to keep the current value. Another year is 400 `validation.failed`.",
            "format": "int32"
          },
          "enteredMileage": {
            "maximum": 100000000,
            "minimum": 0,
            "type": [
              "null",
              "number"
            ],
            "description": "The vehicle's mileage as you record it, from 0 to 100,000,000. Spillard stores no unit. Omit it to keep the current value. Another value is 400 `validation.failed`.",
            "format": "double"
          }
        },
        "description": "Changes to a vehicle. Omitted fields stay unchanged."
      },
      "UpdateWebhookEndpointRequest": {
        "type": "object",
        "properties": {
          "name": {
            "maxLength": 64,
            "minLength": 1,
            "type": [
              "null",
              "string"
            ],
            "description": "The new name, 1 to 64 characters. Omit it to keep the current name."
          },
          "url": {
            "type": [
              "null",
              "string"
            ],
            "description": "The new destination URL. Omit it to keep the current URL.",
            "format": "uri"
          },
          "eventTypes": {
            "minItems": 1,
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "description": "The new full set of webhook types (for example `spillard.event.raised.v1`), at least one. Omit it to keep the current set."
          },
          "eventTypeFilters": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/EventTypeFilter"
            },
            "description": "The new full set of device event type filters (for example `adas.*`). An empty list clears the filters. Omit it to keep the current filters."
          },
          "active": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "The new active flag. Omit it to keep the current value."
          },
          "includeSubOrganizations": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Whether the endpoint also receives the events of every sub-organization of the endpoint's organization. Omit it to keep the current value (an administrator may have set it in Spillard). `true` needs a client that may read that organization's sub-organizations (its entry in `access` of `GET /v1/context` has `includeSubOrganizations: true`); otherwise the answer is 403 `auth.tenant_forbidden`, also when the endpoint has it on already. A client without that permission cannot widen an endpoint that has it on (a new URL, an added type, a wider filter, or activating it) unless the same request sends `false`."
          }
        },
        "description": "Changes to a webhook endpoint. Omitted (null) fields stay unchanged."
      },
      "UpdateWebhookSettingsRequest": {
        "required": [
          "enabled"
        ],
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Required. `true` delivers messages to your active endpoints, including the events of sub-organizations that they receive. `false` stops every delivery."
          }
        },
        "description": "The new state of the webhook delivery switch."
      },
      "Vehicle": {
        "required": [
          "id",
          "fuelType",
          "vehicleType",
          "devices"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The vehicle's id (UUID). `GET /v1/vehicles/{vehicleId}` reads it.",
            "format": "uuid"
          },
          "registrationNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's registration number (number plate). Null when not set."
          },
          "chassisNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's chassis number (VIN). Null when not set."
          },
          "make": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's make (manufacturer). Null when not set."
          },
          "model": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's model name. Null when not set."
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet the vehicle belongs to. Null when it is not in a fleet."
          },
          "driver": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The driver assigned to the vehicle now. Null when there is none."
          },
          "organization": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The organization that owns the vehicle, which is the fleet's organization. Null when the fleet cannot be resolved."
          },
          "fuelType": {
            "type": "string",
            "description": "The fuel type. Never null: `other` when the fuel is another type or none was recorded. Known values: `diesel`, `electric`, `hybrid`, `lpg`, `petrol`, `other`. More values can be added; handle one you do not know."
          },
          "engineSizeLiters": {
            "type": [
              "null",
              "number"
            ],
            "description": "The engine size in liters. Null when unknown.",
            "format": "double"
          },
          "vehicleType": {
            "type": "string",
            "description": "The vehicle type. Never null: `unknown` when none was recorded. Examples: `unknown`, `car`, `van`. See the `VehicleType` model for the values known today. More values can be added; handle one you do not know."
          },
          "trackingDevice": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DeviceReference"
              }
            ],
            "description": "The device that records the vehicle's location. Null when none is attached."
          },
          "mediaDevice": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DeviceReference"
              }
            ],
            "description": "The device that records the vehicle's media. Null when none is attached."
          },
          "yearOfManufacture": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The year the vehicle was made. Null when unknown.",
            "format": "int32"
          },
          "enteredMileage": {
            "type": [
              "null",
              "number"
            ],
            "description": "The mileage entered in Spillard for the vehicle. Null when none was entered. Spillard stores no unit (customers enter miles or kilometers) and does not measure the mileage, so it does not change as the vehicle drives.",
            "format": "double"
          },
          "devices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VehicleDevice"
            },
            "description": "The devices fitted to the vehicle: enabled devices first, then by hardware id. Empty when there are none."
          },
          "connectivityStatus": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's connectivity: that of its most recently reporting device (`online` within 2 minutes, `offline` up to 72 hours, `sleep` after that; the Spillard web app's live map shows `sleep` as Inactive). Null when the vehicle has no device or none could be determined. Known values: `online`, `offline`, `sleep`. More values can be added; handle one you do not know."
          },
          "lastReportedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When Spillard last received data from any device of the vehicle (UTC, ISO 8601). Null when none reported or live data is temporarily unavailable.",
            "format": "date-time"
          },
          "firstReportedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When Spillard first received data from any device of the vehicle (UTC, ISO 8601). Null when none reported.",
            "format": "date-time"
          }
        },
        "description": "A vehicle with its details, driver, devices and connectivity."
      },
      "VehicleDevice": {
        "required": [
          "id",
          "hardwareId",
          "enabled"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The device's id (UUID). Use it as `{deviceId}` in paths.",
            "format": "uuid"
          },
          "hardwareId": {
            "type": "string",
            "description": "The hardware id of the device: the identifier it reports itself with. It is not the `serialNumber`. `{deviceId}` in paths accepts it too."
          },
          "enabled": {
            "type": "boolean",
            "description": "True when Spillard accepts data from the device. `:disable-ingest` stops it and `:enable-ingest` resumes it."
          },
          "connectivityStatus": {
            "type": [
              "null",
              "string"
            ],
            "description": "The device's connectivity, from the time of its last data: `online` within 2 minutes, `offline` up to 72 hours, `sleep` after that or when it never reported (the Spillard web app's live map shows `sleep` as Inactive). Null when it cannot be determined (the device has no hardware id, or live data is temporarily unavailable). Known values: `online`, `offline`, `sleep`. More values can be added; handle one you do not know."
          },
          "lastReportedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When Spillard last received data from the device (UTC, ISO 8601). Null when it never reported or live data is temporarily unavailable.",
            "format": "date-time"
          },
          "firstReportedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When Spillard first received data from the device (UTC, ISO 8601). Null when it never reported.",
            "format": "date-time"
          }
        },
        "description": "A device fitted to a vehicle, with its connectivity."
      },
      "VehiclePage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VehicleSummary"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of vehicles."
      },
      "VehiclePosition": {
        "required": [
          "vehicle",
          "organization",
          "recordedAt",
          "connectivityStatus",
          "location",
          "gpsValid"
        ],
        "type": "object",
        "properties": {
          "vehicle": {
            "description": "The vehicle this position belongs to.",
            "$ref": "#/components/schemas/VehicleReference"
          },
          "device": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DeviceReference"
              }
            ],
            "description": "The reporting device. Null when the position has no hardware id."
          },
          "organization": {
            "description": "The organization that owns the vehicle, which is the fleet's organization.",
            "$ref": "#/components/schemas/IdReference"
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The vehicle's fleet. Null when it is not in a fleet."
          },
          "driver": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The driver assigned to the vehicle. Null when there is none. Spillard does not know who is driving."
          },
          "recordedAt": {
            "type": "string",
            "description": "When the device recorded this position (UTC, ISO 8601).",
            "format": "date-time"
          },
          "connectivityStatus": {
            "type": "string",
            "description": "The connectivity of the vehicle's tracking device, computed from `recordedAt`: `online` within 2 minutes, `offline` up to 72 hours, `sleep` after that (the Spillard web app's live map shows `sleep` as Inactive). Known values: `online`, `offline`, `sleep`. More values can be added; handle one you do not know."
          },
          "location": {
            "description": "The last valid location. It is `0,0` when no fix was ever stored, and then `gpsValid` is false.",
            "$ref": "#/components/schemas/GeoPoint"
          },
          "speedKph": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The ground speed in km/h. Null when not reported.",
            "format": "int32"
          },
          "headingDegrees": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The heading in degrees, 0 to 359. Null when not reported.",
            "format": "int32"
          },
          "gpsValid": {
            "type": "boolean",
            "description": "False when the location is `0,0` because no fix was ever stored. Otherwise true. The location can be older than `recordedAt`."
          }
        },
        "description": "A vehicle's latest position. Spillard keeps valid GPS fixes only. When the device reports without a fix, the\nprevious location stays and `recordedAt` is the time of the latest report."
      },
      "VehiclePositionPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VehiclePosition"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          }
        },
        "description": "A page of vehicle positions."
      },
      "VehicleReference": {
        "required": [
          "id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The vehicle's id (UUID). `GET /v1/vehicles/{vehicleId}` reads it.",
            "format": "uuid"
          },
          "registrationNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's registration number (number plate). Null when not set. Webhook messages call it `registration`."
          },
          "chassisNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's chassis number (VIN). Null when not set."
          }
        },
        "description": "A vehicle reference: the vehicle's id, registration number and chassis number."
      },
      "VehicleSummary": {
        "required": [
          "id",
          "devices"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The vehicle's id (UUID). `GET /v1/vehicles/{vehicleId}` reads it.",
            "format": "uuid"
          },
          "registrationNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's registration number (number plate). Null when not set."
          },
          "chassisNumber": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's chassis number (VIN). Null when not set."
          },
          "make": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's make (manufacturer). Null when not set."
          },
          "model": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's model name. Null when not set."
          },
          "fleet": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The fleet the vehicle belongs to. Null when it is not in a fleet."
          },
          "driver": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The driver assigned to the vehicle now. Null when there is none."
          },
          "organization": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdReference"
              }
            ],
            "description": "The organization that owns the vehicle, which is the fleet's organization. Null when the fleet cannot be resolved."
          },
          "yearOfManufacture": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The year the vehicle was made. Null when unknown.",
            "format": "int32"
          },
          "enteredMileage": {
            "type": [
              "null",
              "number"
            ],
            "description": "The mileage entered in Spillard for the vehicle. Null when none was entered. Spillard stores no unit (customers enter miles or kilometers) and does not measure the mileage, so it does not change as the vehicle drives.",
            "format": "double"
          },
          "devices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VehicleDevice"
            },
            "description": "The devices fitted to the vehicle: enabled devices first, then by hardware id. Empty when there are none."
          },
          "connectivityStatus": {
            "type": [
              "null",
              "string"
            ],
            "description": "The vehicle's connectivity: that of its most recently reporting device (`online` within 2 minutes, `offline` up to 72 hours, `sleep` after that; the Spillard web app's live map shows `sleep` as Inactive). Null when the vehicle has no device or none could be determined. Known values: `online`, `offline`, `sleep`. More values can be added; handle one you do not know."
          },
          "lastReportedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When Spillard last received data from any device of the vehicle (UTC, ISO 8601). Null when none reported or live data is temporarily unavailable.",
            "format": "date-time"
          },
          "firstReportedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When Spillard first received data from any device of the vehicle (UTC, ISO 8601). Null when none reported.",
            "format": "date-time"
          }
        },
        "description": "A vehicle as lists show it."
      },
      "VehicleType": {
        "enum": [
          "unknown",
          "car",
          "van",
          "tractor_trailer",
          "small_rigid_truck",
          "road_sweeper",
          "tracked_excavator",
          "wheeled_excavator",
          "dozer",
          "heavy_dump_truck",
          "articulated_dump_truck",
          "site_dumper",
          "loading_shovel",
          "roller",
          "backhoe_loader",
          "telehandler"
        ],
        "type": "string",
        "description": "The type of a vehicle.",
        "x-enumDescriptions": {
          "unknown": "Not recorded.",
          "car": "Car.",
          "van": "Van.",
          "tractor_trailer": "Tractor trailer.",
          "small_rigid_truck": "Small rigid truck.",
          "road_sweeper": "Road sweeper.",
          "tracked_excavator": "Tracked excavator.",
          "wheeled_excavator": "Wheeled excavator.",
          "dozer": "Dozer.",
          "heavy_dump_truck": "Heavy dump truck.",
          "articulated_dump_truck": "Articulated dump truck.",
          "site_dumper": "Site dumper.",
          "loading_shovel": "Loading shovel.",
          "roller": "Roller.",
          "backhoe_loader": "Backhoe loader.",
          "telehandler": "Telehandler."
        }
      },
      "VideoQuality": {
        "enum": [
          "sd",
          "hd"
        ],
        "type": "string",
        "description": "The video quality of a video request.",
        "x-enumDescriptions": {
          "sd": "Standard definition: the sub stream of the device (Sub stream in the Spillard web app).",
          "hd": "High definition: the main stream of the device (Main stream in the web app)."
        }
      },
      "WebhookAddress": {
        "type": "object",
        "properties": {
          "label": {
            "type": [
              "null",
              "string"
            ],
            "description": "The formatted, single-line address."
          },
          "street": {
            "type": [
              "null",
              "string"
            ],
            "description": "The street, or null when unknown."
          },
          "city": {
            "type": [
              "null",
              "string"
            ],
            "description": "The city, or null when unknown."
          },
          "postalCode": {
            "type": [
              "null",
              "string"
            ],
            "description": "The postal code, or null when unknown."
          },
          "countryCode": {
            "type": [
              "null",
              "string"
            ],
            "description": "The country code (for example `GBR`), or null when unknown."
          }
        },
        "description": "A postal address. When only a formatted address is known, `label` carries it and the other fields are null."
      },
      "WebhookDelivery": {
        "required": [
          "deliveryId",
          "endpointId",
          "organizationId",
          "sourceOrganizationId",
          "eventType",
          "status",
          "replayable"
        ],
        "type": "object",
        "properties": {
          "deliveryId": {
            "type": "string",
            "description": "The delivery's `webhook-id`. It equals the CloudEvents `id` of the message. An opaque id: treat it as text of up to 255 characters; do not parse it."
          },
          "endpointId": {
            "type": "string",
            "description": "The endpoint the delivery is addressed to."
          },
          "organizationId": {
            "type": "string",
            "description": "The organization the delivery belongs to: the organization that owns the endpoint.",
            "format": "uuid"
          },
          "sourceOrganizationId": {
            "type": "string",
            "description": "The organization the event belongs to (the message's `source`). It equals `organizationId` for your organization's own events. For an event the endpoint received because it includes sub-organizations, it is the id (UUID) of that sub-organization.",
            "format": "uuid"
          },
          "eventType": {
            "type": "string",
            "description": "A webhook type: the CloudEvents `type` of a webhook message. Known values: `spillard.alarm.raised.v1`, `spillard.event.raised.v1`, `spillard.journey.completed.v1`, `spillard.telemetry.live.v1`, `spillard.telemetry.history.v1`, `spillard.media.request.completed.v1`. More values can be added; handle one you do not know."
          },
          "status": {
            "type": "string",
            "description": "`pending` or `failed`. Known values: `pending`, `failed`. More values can be added; handle one you do not know."
          },
          "attemptCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The number of failed attempts so far for a pending delivery. 0 when a replay has not been attempted yet. Null for a failed delivery.",
            "format": "int32"
          },
          "lastAttemptAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the latest failed attempt ran (UTC, ISO 8601). Null when unknown or when no attempt has run yet.",
            "format": "date-time"
          },
          "lastResponseStatusCode": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The HTTP status code of the latest failed attempt. Null when there was no response.",
            "format": "int32"
          },
          "nextAttemptAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When Spillard attempts a pending delivery next (UTC, ISO 8601). Null for a failed delivery.",
            "format": "date-time"
          },
          "failedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the delivery failed for good (UTC, ISO 8601). Null for a pending delivery.",
            "format": "date-time"
          },
          "failureReason": {
            "type": [
              "null",
              "string"
            ],
            "description": "Why a failed delivery stopped. `retries_exhausted`: the retry window ended. `permanent_failure`: the endpoint answered with a status other than 408, 429 or 5xx, or the destination is blocked. `disabled`: webhooks, the endpoint or the type were switched off, or, for a sub-organization's event, the endpoint stopped including sub-organizations. `filtered`: the message no longer matched `eventTypeFilters`. `unresolvable`: the retry could not be prepared, for example because the webhook type is no longer delivered. Null for a pending delivery. Known values: `retries_exhausted`, `permanent_failure`, `disabled`, `filtered`, `unresolvable`. More values can be added; handle one you do not know."
          },
          "replayable": {
            "type": "boolean",
            "description": "True when `:replay` can send the delivery again. This holds for a failed delivery whose message was kept in full."
          }
        },
        "description": "A delivery to the endpoint that has not succeeded. `pending`: an attempt failed or a replay was requested, and\nSpillard attempts it again at `nextAttemptAt`. `failed`: retries ended, and Spillard does not attempt it\nagain unless you replay it. It never carries the message body."
      },
      "WebhookDeliveryFailureReason": {
        "enum": [
          "retries_exhausted",
          "permanent_failure",
          "disabled",
          "filtered",
          "unresolvable"
        ],
        "type": "string",
        "description": "Why a failed delivery stopped.",
        "x-enumDescriptions": {
          "retries_exhausted": "The retry window ended.",
          "permanent_failure": "The receiver answered with a status other than 2xx, 408, 429 or 5xx (redirects are not followed), or the destination is blocked.",
          "disabled": "Webhooks, the endpoint or the type were switched off, or the endpoint stopped including sub-organizations.",
          "filtered": "The device event no longer matched `eventTypeFilters`.",
          "unresolvable": "The retry could not be prepared (for example the webhook type is no longer delivered). The message is kept and can be replayed."
        }
      },
      "WebhookDeliveryPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDelivery"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of webhook deliveries."
      },
      "WebhookDeliveryReplayAccepted": {
        "required": [
          "deliveryId",
          "requeued"
        ],
        "type": "object",
        "properties": {
          "deliveryId": {
            "type": "string",
            "description": "The replayed delivery's `webhook-id`. It does not change, so receivers can deduplicate on it."
          },
          "requeued": {
            "type": "boolean",
            "description": "True when the failed delivery was queued again. False when it was already pending."
          }
        },
        "description": "An accepted replay of a delivery."
      },
      "WebhookDeliveryStatus": {
        "enum": [
          "pending",
          "failed"
        ],
        "type": "string",
        "description": "The state of a delivery in the delivery log.",
        "x-enumDescriptions": {
          "pending": "At least one attempt failed, or a replay was requested; it is attempted again at `nextAttemptAt`.",
          "failed": "Retries ended; it is not attempted again unless replayed."
        }
      },
      "WebhookEndpoint": {
        "required": [
          "id",
          "organizationId",
          "name",
          "url",
          "eventTypes",
          "active",
          "includeSubOrganizations",
          "createdAt",
          "updatedAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The endpoint's id. Use it in the paths of the endpoint and its sub-resources."
          },
          "organizationId": {
            "type": "string",
            "description": "The organization the endpoint belongs to: your client's organization, or the one `organizationId` named.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The endpoint's name, 1 to 64 characters, unique within the organization."
          },
          "url": {
            "type": "string",
            "description": "The HTTPS URL Spillard sends signed messages to with POST.",
            "format": "uri"
          },
          "eventTypes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The webhook types the endpoint is subscribed to, for example `spillard.event.raised.v1`. These are not device event types. Known values: `spillard.alarm.raised.v1`, `spillard.event.raised.v1`, `spillard.journey.completed.v1`, `spillard.telemetry.live.v1`, `spillard.telemetry.history.v1`, `spillard.media.request.completed.v1`. More values can be added; handle one you do not know."
          },
          "eventTypeFilters": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "The device event types, for example `adas.*`, that `spillard.event.raised.v1` messages are narrowed to. Null delivers every device event. Examples: `adas.bridge_detected`, `adas.bridge_recognition`, `adas.bridge_stop`. See the `EventTypeFilter` model for the values known today. More values can be added; handle one you do not know."
          },
          "active": {
            "type": "boolean",
            "description": "True when the endpoint receives deliveries."
          },
          "includeSubOrganizations": {
            "type": "boolean",
            "description": "True when the endpoint also receives the events of every sub-organization of its organization. Their messages name the sub-organization in `source`."
          },
          "secretHint": {
            "type": [
              "null",
              "string"
            ],
            "description": "The last 4 characters of the signing secret. Null while the endpoint has no secret."
          },
          "createdAt": {
            "type": "string",
            "description": "When the endpoint was created (UTC, ISO 8601).",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "When the endpoint last changed (UTC, ISO 8601).",
            "format": "date-time"
          },
          "previousSecretHint": {
            "type": [
              "null",
              "string"
            ],
            "description": "The last 4 characters of the previous signing secret, while it still signs deliveries after a rotation. Null otherwise."
          },
          "previousSecretExpiresAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the previous signing secret stops signing deliveries (UTC, ISO 8601). Null when no previous secret is active.",
            "format": "date-time"
          }
        },
        "description": "A webhook endpoint. Its signing secret is never returned; `secretHint` shows its last 4 characters. The\nendpoint belongs to your client's organization, or to the organization named by `organizationId` (one your\nclient was granted with every fleet). While `includeSubOrganizations` is true, the endpoint also receives the\nevents of every sub-organization of its own organization. Those messages name the sub-organization in `source`. The webhook\nsettings of the endpoint's organization switch them on and off."
      },
      "WebhookEndpointCreated": {
        "required": [
          "id",
          "organizationId",
          "name",
          "url",
          "eventTypes",
          "active",
          "includeSubOrganizations",
          "createdAt",
          "signingSecret"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The id of the new endpoint."
          },
          "organizationId": {
            "type": "string",
            "description": "The organization the endpoint was created in.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The endpoint's name."
          },
          "url": {
            "type": "string",
            "description": "The HTTPS URL Spillard sends signed messages to with POST.",
            "format": "uri"
          },
          "eventTypes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The webhook types the endpoint is subscribed to, for example `spillard.event.raised.v1`. These are not device event types. Known values: `spillard.alarm.raised.v1`, `spillard.event.raised.v1`, `spillard.journey.completed.v1`, `spillard.telemetry.live.v1`, `spillard.telemetry.history.v1`, `spillard.media.request.completed.v1`. More values can be added; handle one you do not know."
          },
          "eventTypeFilters": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "The device event types, for example `adas.*`, that `spillard.event.raised.v1` messages are narrowed to. Null delivers every device event. Examples: `adas.bridge_detected`, `adas.bridge_recognition`, `adas.bridge_stop`. See the `EventTypeFilter` model for the values known today. More values can be added; handle one you do not know."
          },
          "active": {
            "type": "boolean",
            "description": "True when the endpoint receives deliveries."
          },
          "includeSubOrganizations": {
            "type": "boolean",
            "description": "True when the endpoint also receives the events of every sub-organization of its organization."
          },
          "secretHint": {
            "type": [
              "null",
              "string"
            ],
            "description": "The last 4 characters of the signing secret."
          },
          "createdAt": {
            "type": "string",
            "description": "When the endpoint was created (UTC, ISO 8601).",
            "format": "date-time"
          },
          "signingSecret": {
            "type": "string",
            "description": "The signing secret (Standard Webhooks). It is shown only in this response."
          }
        },
        "description": "A new webhook endpoint with its signing secret (`whsec_` followed by base64). The secret is shown only once, so\nstore it safely."
      },
      "WebhookEndpointPage": {
        "required": [
          "items",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            },
            "description": "The items of this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass it as `cursor` to read the next page, with the same filters; null on the last page."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows."
          },
          "totalCount": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The exact number of matching items when the request sent `count=true`; otherwise null.",
            "format": "int64"
          }
        },
        "description": "A page of webhook endpoints."
      },
      "WebhookEndpointSecretRotated": {
        "required": [
          "signingSecret",
          "secretHint"
        ],
        "type": "object",
        "properties": {
          "signingSecret": {
            "type": "string",
            "description": "The new signing secret. It is shown only in this response."
          },
          "secretHint": {
            "type": "string",
            "description": "The last 4 characters of the new signing secret."
          },
          "previousSecretExpiresAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the previous secret stops signing deliveries (UTC, ISO 8601). Null when it stopped at once.",
            "format": "date-time"
          }
        },
        "description": "A new signing secret. It is shown only once. The previous secret keeps signing deliveries until\n`previousSecretExpiresAt` (24 hours by default). The new secret starts signing within about a minute. From then\nuntil `previousSecretExpiresAt`, every delivery carries two `v1` signatures in `webhook-signature`,\nthe new secret's first. A receiver that verifies with both secrets until then rejects no delivery."
      },
      "WebhookEventType": {
        "enum": [
          "spillard.alarm.raised.v1",
          "spillard.event.raised.v1",
          "spillard.journey.completed.v1",
          "spillard.telemetry.live.v1",
          "spillard.telemetry.history.v1",
          "spillard.media.request.completed.v1"
        ],
        "type": "string",
        "description": "A webhook type: the CloudEvents `type` of a webhook message.",
        "x-enumDescriptions": {
          "spillard.alarm.raised.v1": "A device raised an alarm that Spillard accepted. `kind` says what raised it; `eventId` is the event Spillard created from it, or null. That event is also delivered as a `spillard.event.raised.v1` message to the endpoints subscribed to that type.",
          "spillard.event.raised.v1": "A device raised an event. `eventTypes` holds its device event types as `family.name` tokens (for example `dsm.fatigue`); an endpoint can filter on them with `eventTypeFilters`. `links.self` is `/v1/events/{eventId}` and `links.media` is `/v1/events/{eventId}/media`.",
          "spillard.journey.completed.v1": "A journey was completed or reprocessed. The latest `revision` wins; delete the ids in `replaces`. No `driver` in v1: Spillard does not know who drove a journey.",
          "spillard.telemetry.live.v1": "The latest positions of the organization's vehicles, batched every 5 seconds.",
          "spillard.telemetry.history.v1": "Recorded telemetry tracks of the organization's devices, batched, late uploads included. Tracks carry no `driver` in v1.",
          "spillard.media.request.completed.v1": "A video request ended with `outcome` completed, failed or no_data. Only `completed` is reliable today; `failed` and `no_data` are in future development, do not rely on them. Poll `GET /v1/media-requests/{mediaRequestId}` until `state` is final: a request that ends without an answer from the device may send no message. `links.media` points to `/v1/media-requests/{mediaRequestId}/media`."
        }
      },
      "WebhookEventTypeCatalog": {
        "required": [
          "eventTypes"
        ],
        "type": "object",
        "properties": {
          "eventTypes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventTypeEntry"
            },
            "description": "The webhook types an endpoint can subscribe to."
          }
        },
        "description": "The webhook types an endpoint can subscribe to."
      },
      "WebhookEventTypeEntry": {
        "required": [
          "type",
          "description",
          "samplePayloadRef"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "A webhook type: the CloudEvents `type` of a webhook message. Known values: `spillard.alarm.raised.v1`, `spillard.event.raised.v1`, `spillard.journey.completed.v1`, `spillard.telemetry.live.v1`, `spillard.telemetry.history.v1`, `spillard.media.request.completed.v1`. More values can be added; handle one you do not know."
          },
          "description": {
            "type": "string",
            "description": "What the type reports, and when Spillard sends it."
          },
          "samplePayloadRef": {
            "type": "string",
            "description": "A JSON pointer to the schema of the type's message in this document."
          }
        },
        "description": "A webhook type: its CloudEvents `type` (a value of `eventTypes`), what it reports, and a JSON pointer to\nits message schema."
      },
      "WebhookMessageExamples": {
        "required": [
          "alarmRaised",
          "eventRaised",
          "journeyCompleted",
          "telemetryLive",
          "telemetryHistory",
          "mediaRequestCompleted"
        ],
        "type": "object",
        "properties": {
          "alarmRaised": {
            "description": "An example `spillard.alarm.raised.v1` message.",
            "$ref": "#/components/schemas/AlarmRaisedMessage"
          },
          "eventRaised": {
            "description": "An example `spillard.event.raised.v1` message.",
            "$ref": "#/components/schemas/EventRaisedMessage"
          },
          "journeyCompleted": {
            "description": "An example `spillard.journey.completed.v1` message.",
            "$ref": "#/components/schemas/JourneyCompletedMessage"
          },
          "telemetryLive": {
            "description": "An example `spillard.telemetry.live.v1` message.",
            "$ref": "#/components/schemas/TelemetryLiveMessage"
          },
          "telemetryHistory": {
            "description": "An example `spillard.telemetry.history.v1` message.",
            "$ref": "#/components/schemas/TelemetryHistoryMessage"
          },
          "mediaRequestCompleted": {
            "description": "An example `spillard.media.request.completed.v1` message.",
            "$ref": "#/components/schemas/MediaRequestCompletedMessage"
          }
        },
        "description": "One example message per webhook type, each a CloudEvents 1.0 envelope. It gives generated clients a typed model for\nevery webhook message. `GET /v1/webhooks/message-schemas` returns it."
      },
      "WebhookPlace": {
        "type": "object",
        "properties": {
          "location": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/GeoPoint"
              }
            ],
            "description": "The coordinates of the place, or null when unknown."
          },
          "address": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/WebhookAddress"
              }
            ],
            "description": "The street address of the place, or null when unknown."
          }
        },
        "description": "Where a journey started or ended: its coordinates and street address."
      },
      "WebhookSettings": {
        "required": [
          "organizationId",
          "enabled",
          "endpointLimit"
        ],
        "type": "object",
        "properties": {
          "organizationId": {
            "type": "string",
            "description": "The organization the settings belong to: your client's organization, or the one `organizationId` named.",
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean",
            "description": "`true`: messages are delivered to your active endpoints, including the events of sub-organizations that they receive. `false`: nothing is delivered."
          },
          "endpointLimit": {
            "type": "integer",
            "description": "The number of webhook endpoints the organization can have.",
            "format": "int32",
            "readOnly": true
          }
        },
        "description": "Your organization's webhook delivery switch, and how many endpoints it can have."
      },
      "WebhookTestOutcome": {
        "enum": [
          "delivered",
          "rejected",
          "unreachable",
          "blocked"
        ],
        "type": "string",
        "description": "The outcome of a test message.",
        "x-enumDescriptions": {
          "delivered": "The receiver answered 2xx.",
          "rejected": "The receiver answered with another status.",
          "unreachable": "No response: a connection failure, or no response within 10 seconds.",
          "blocked": "The destination is not a public HTTPS address; nothing was sent."
        }
      },
      "WebhookTestRequest": {
        "type": "object",
        "properties": {
          "eventType": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/WebhookEventType"
              }
            ],
            "description": "The webhook type of the test message, for example `spillard.alarm.raised.v1`. Omit it to send the endpoint's first subscribed type."
          }
        },
        "description": "Which webhook type to test. The body is optional."
      },
      "WebhookTestResult": {
        "required": [
          "deliveryId",
          "eventType",
          "outcome",
          "latencyMilliseconds"
        ],
        "type": "object",
        "properties": {
          "deliveryId": {
            "type": "string",
            "description": "The test message's `webhook-id`. It equals the CloudEvents `id` of the message."
          },
          "eventType": {
            "type": "string",
            "description": "A webhook type: the CloudEvents `type` of a webhook message. Known values: `spillard.alarm.raised.v1`, `spillard.event.raised.v1`, `spillard.journey.completed.v1`, `spillard.telemetry.live.v1`, `spillard.telemetry.history.v1`, `spillard.media.request.completed.v1`. More values can be added; handle one you do not know."
          },
          "outcome": {
            "type": "string",
            "description": "`delivered` (a 2xx response), `rejected` (any other response status), `unreachable` (no response: a connection failure, or no response within 10 seconds) or `blocked` (the destination is not a public HTTPS address; nothing was sent). Known values: `delivered`, `rejected`, `unreachable`, `blocked`. More values can be added; handle one you do not know."
          },
          "responseStatusCode": {
            "type": [
              "null",
              "integer"
            ],
            "description": "The HTTP status code the endpoint returned. Null when there was no response.",
            "format": "int32"
          },
          "latencyMilliseconds": {
            "type": "integer",
            "description": "The time from sending the request to receiving the response headers, or to giving up, in milliseconds.",
            "format": "int64"
          }
        },
        "description": "The result of a test message: the outcome only, never the receiver's response body or headers."
      },
      "WebhookVehicleReference": {
        "required": [
          "id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The vehicle's id: `GET /v1/vehicles/{vehicleId}` reads it.",
            "format": "uuid"
          },
          "registration": {
            "type": [
              "null",
              "string"
            ],
            "description": "The registration number (number plate); null when unknown. It is `registrationNumber` in the REST API."
          }
        },
        "description": "A reference to a vehicle: its id and registration number. A missing vehicle is `null` in place of the whole\nreference, never `{ \"id\": null }`."
      }
    },
    "parameters": {
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "Where the page starts: the `nextCursor` of the previous page. Leave it out for the first page, and keep the other query parameters the same while you page. The value is opaque: do not build or change it.",
        "schema": {
          "type": "string"
        }
      },
      "Count": {
        "name": "count",
        "in": "query",
        "description": "Set to `true` to receive `totalCount`, the number of matching items. It costs an extra query and 5 extra rate-limit units, so ask for it only when you need the number, on the first page. `GET /v1/devices` and `GET /v1/vehicles` answer 400 `validation.failed` when it comes with `connectivityStatus` or `lastReportedFrom`.",
        "schema": {
          "type": "boolean",
          "default": false
        }
      },
      "ManagementLimit": {
        "name": "limit",
        "in": "query",
        "description": "Items per page: 1 to 100, 50 when not given. A larger value is reduced to 100; a value below 1 is 400 `validation.failed`.",
        "schema": {
          "maximum": 100,
          "minimum": 1,
          "type": "integer",
          "format": "int32",
          "default": 50
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "description": "Items per page: 1 to 1,000, 100 when not given. A larger value is reduced to 1,000; a value below 1 is 400 `validation.failed`.",
        "schema": {
          "maximum": 1000,
          "minimum": 1,
          "type": "integer",
          "format": "int32",
          "default": 100
        }
      },
      "EventLimit": {
        "name": "limit",
        "in": "query",
        "description": "Items per page: 1 to 500, 100 when not given. A larger value is reduced to 500; a value below 1 is 400 `validation.failed`.",
        "schema": {
          "maximum": 500,
          "minimum": 1,
          "type": "integer",
          "format": "int32",
          "default": 100
        }
      },
      "FrameLimit": {
        "name": "limit",
        "in": "query",
        "description": "Items per page: 1 to 10,000, 1,000 when not given. A larger value is reduced to 10,000; a value below 1 is 400 `validation.failed`.",
        "schema": {
          "maximum": 10000,
          "minimum": 1,
          "type": "integer",
          "format": "int32",
          "default": 1000
        }
      }
    },
    "examples": {
      "api_client.not_found": {
        "summary": "api_client.not_found",
        "description": "The API client does not exist or is outside your client's access. Check the id.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "The API client does not exist or is outside your client's access. Check the id.",
          "instance": "/v1/api-clients/sl-4f1c8e2a9b3d47c6a5e0d9b8c7f61a23",
          "errorCode": "api_client.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "audit_form.required": {
        "summary": "audit_form.required",
        "description": "The fleet requires a video audit form for this action, which the API cannot submit. Do it in the Spillard web app.",
        "value": {
          "type": "https://connect.spillard.live/errors/422",
          "title": "Unprocessable Entity",
          "status": 422,
          "detail": "The fleet requires a video audit form for this action, which the API cannot submit. Do it in the Spillard web app.",
          "instance": "/v1/media-requests",
          "errorCode": "audit_form.required",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "auth.fleet_restricted": {
        "summary": "auth.fleet_restricted",
        "description": "Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
        "value": {
          "type": "https://connect.spillard.live/errors/403",
          "title": "Forbidden",
          "status": 403,
          "detail": "Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full.",
          "instance": "/v1/fleets",
          "errorCode": "auth.fleet_restricted",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "auth.scope_missing": {
        "summary": "auth.scope_missing",
        "description": "The token lacks the scope the operation requires, or the request names a scope your client does not hold. Grant the scope to the API client and get a new token.",
        "value": {
          "type": "https://connect.spillard.live/errors/403",
          "title": "Forbidden",
          "status": 403,
          "detail": "The token lacks the scope the operation requires, or the request names a scope your client does not hold. Grant the scope to the API client and get a new token.",
          "instance": "/v1/vehicles",
          "errorCode": "auth.scope_missing",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "auth.tenant_forbidden": {
        "summary": "auth.tenant_forbidden",
        "description": "The organization is outside your client's access, the request needs sub-organization access your client lacks, or the token's access list is damaged. Check `access` in `GET /v1/context`, or ask your Spillard account manager to change your client's access.",
        "value": {
          "type": "https://connect.spillard.live/errors/403",
          "title": "Forbidden",
          "status": 403,
          "detail": "The organization is outside your client's access, the request needs sub-organization access your client lacks, or the token's access list is damaged. Check `access` in `GET /v1/context`, or ask your Spillard account manager to change your client's access.",
          "instance": "/v1/vehicles",
          "errorCode": "auth.tenant_forbidden",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "auth.token_invalid": {
        "summary": "auth.token_invalid",
        "description": "The bearer token is missing, malformed, expired or issued for another API. Get a new token.",
        "value": {
          "type": "https://connect.spillard.live/errors/401",
          "title": "Unauthorized",
          "status": 401,
          "detail": "The bearer token is missing, malformed, expired or issued for another API. Get a new token.",
          "instance": "/v1/vehicles",
          "errorCode": "auth.token_invalid",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "command.not_found": {
        "summary": "command.not_found",
        "description": "The command or media request does not exist or is outside your client's access. Check the id. The media request routes answer with this code too.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "The command or media request does not exist or is outside your client's access. Check the id. The media request routes answer with this code too.",
          "instance": "/v1/media-requests/9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683",
          "errorCode": "command.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "concurrency.etag_mismatch": {
        "summary": "concurrency.etag_mismatch",
        "description": "The resource changed since you read it: `If-Match` no longer matches its `ETag`. Read it again, then retry the update.",
        "value": {
          "type": "https://connect.spillard.live/errors/412",
          "title": "Precondition Failed",
          "status": 412,
          "detail": "The resource changed since you read it: `If-Match` no longer matches its `ETag`. Read it again, then retry the update.",
          "instance": "/v1/vehicles/7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
          "errorCode": "concurrency.etag_mismatch",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "data.retention_restricted": {
        "summary": "data.retention_restricted",
        "description": "The data is older than the organization's data access allows (see `dataAccessDays` in `GET /v1/context`). Ask for newer data.",
        "value": {
          "type": "https://connect.spillard.live/errors/403",
          "title": "Forbidden",
          "status": 403,
          "detail": "The data is older than the organization's data access allows (see `dataAccessDays` in `GET /v1/context`). Ask for newer data.",
          "instance": "/v1/events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
          "errorCode": "data.retention_restricted",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "dependency.timeout": {
        "summary": "dependency.timeout",
        "description": "A Spillard service did not answer in time. Retry with backoff.",
        "value": {
          "type": "https://connect.spillard.live/errors/504",
          "title": "Gateway Timeout",
          "status": 504,
          "detail": "A Spillard service did not answer in time. Retry with backoff.",
          "instance": "/v1/vehicles",
          "errorCode": "dependency.timeout",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": true
        }
      },
      "dependency.unavailable": {
        "summary": "dependency.unavailable",
        "description": "A Spillard service is temporarily unavailable. Retry with backoff.",
        "value": {
          "type": "https://connect.spillard.live/errors/503",
          "title": "Service Unavailable",
          "status": 503,
          "detail": "A Spillard service is temporarily unavailable. Retry with backoff.",
          "instance": "/v1/vehicles",
          "errorCode": "dependency.unavailable",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": true
        }
      },
      "device.command_not_supported": {
        "summary": "device.command_not_supported",
        "description": "The device does not support the requested command. Choose a command the device supports.",
        "value": {
          "type": "https://connect.spillard.live/errors/400",
          "title": "Bad Request",
          "status": 400,
          "detail": "The device does not support the requested command. Choose a command the device supports.",
          "instance": "/v1/media-requests",
          "errorCode": "device.command_not_supported",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "device.not_found": {
        "summary": "device.not_found",
        "description": "The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle.",
          "instance": "/v1/devices/3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
          "errorCode": "device.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "device_model.not_found": {
        "summary": "device_model.not_found",
        "description": "No device model has this id. Check the id.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "No device model has this id. Check the id.",
          "instance": "/v1/device-models/b7e3a1c9-5d2f-4e86-9c0a-1f4d7b2e6a58",
          "errorCode": "device_model.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "device_protocol.not_found": {
        "summary": "device_protocol.not_found",
        "description": "No device protocol has this id. Check the id.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "No device protocol has this id. Check the id.",
          "instance": "/v1/device-protocols/d1f6c8a2-3b7e-4c59-8e0d-9a2b5f7c4e16",
          "errorCode": "device_protocol.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "driver.not_found": {
        "summary": "driver.not_found",
        "description": "The driver does not exist or is outside your client's access. Check the id. On a vehicle route it can also mean the driver does not drive that vehicle.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "The driver does not exist or is outside your client's access. Check the id. On a vehicle route it can also mean the driver does not drive that vehicle.",
          "instance": "/v1/drivers/c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945",
          "errorCode": "driver.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "event.not_found": {
        "summary": "event.not_found",
        "description": "The event does not exist or is outside your client's access. Check the id.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "The event does not exist or is outside your client's access. Check the id.",
          "instance": "/v1/events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
          "errorCode": "event.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "event.share_restricted": {
        "summary": "event.share_restricted",
        "description": "The event cannot be shared by link: its page would show data outside what your client may read. Do not share this event, or ask your Spillard account manager for wider access for your client.",
        "value": {
          "type": "https://connect.spillard.live/errors/403",
          "title": "Forbidden",
          "status": 403,
          "detail": "The event cannot be shared by link: its page would show data outside what your client may read. Do not share this event, or ask your Spillard account manager for wider access for your client.",
          "instance": "/v1/events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53:share",
          "errorCode": "event.share_restricted",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "fleet.not_found": {
        "summary": "fleet.not_found",
        "description": "The fleet does not exist or is outside your client's access. Check the id.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "The fleet does not exist or is outside your client's access. Check the id.",
          "instance": "/v1/fleets/a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35",
          "errorCode": "fleet.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "idempotency.in_progress": {
        "summary": "idempotency.in_progress",
        "description": "The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.",
        "value": {
          "type": "https://connect.spillard.live/errors/409",
          "title": "Conflict",
          "status": 409,
          "detail": "The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key.",
          "instance": "/v1/fleets",
          "errorCode": "idempotency.in_progress",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "idempotency.key_conflict": {
        "summary": "idempotency.key_conflict",
        "description": "The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
        "value": {
          "type": "https://connect.spillard.live/errors/409",
          "title": "Conflict",
          "status": 409,
          "detail": "The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key.",
          "instance": "/v1/fleets",
          "errorCode": "idempotency.key_conflict",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "journey.not_found": {
        "summary": "journey.not_found",
        "description": "The journey does not exist or is outside your client's access. Check the id.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "The journey does not exist or is outside your client's access. Check the id.",
          "instance": "/v1/journeys/20260929T065804000Z009a2c41f7",
          "errorCode": "journey.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "organization.not_found": {
        "summary": "organization.not_found",
        "description": "The organization does not exist or is outside your client's access. Check the id.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "The organization does not exist or is outside your client's access. Check the id.",
          "instance": "/v1/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
          "errorCode": "organization.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "precondition.required": {
        "summary": "precondition.required",
        "description": "A required header is missing: `If-Match` when you update a vehicle or a driver, `Idempotency-Key` when you create a media request. Send it and retry.",
        "value": {
          "type": "https://connect.spillard.live/errors/428",
          "title": "Precondition Required",
          "status": 428,
          "detail": "A required header is missing: `If-Match` when you update a vehicle or a driver, `Idempotency-Key` when you create a media request. Send it and retry.",
          "instance": "/v1/media-requests",
          "errorCode": "precondition.required",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "rate.limit_exceeded": {
        "summary": "rate.limit_exceeded",
        "description": "The API client sent more requests than its rate limit allows. Wait for `Retry-After` seconds, then retry.",
        "value": {
          "type": "https://connect.spillard.live/errors/429",
          "title": "Too Many Requests",
          "status": 429,
          "detail": "The API client sent more requests than its rate limit allows. Wait for `Retry-After` seconds, then retry.",
          "instance": "/v1/vehicles",
          "errorCode": "rate.limit_exceeded",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": true
        }
      },
      "request.unsupported_media_type": {
        "summary": "request.unsupported_media_type",
        "description": "The body is not JSON. Send it with `Content-Type: application/json`.",
        "value": {
          "type": "https://connect.spillard.live/errors/415",
          "title": "Unsupported Media Type",
          "status": 415,
          "detail": "The body is not JSON. Send it with `Content-Type: application/json`.",
          "instance": "/v1/vehicles",
          "errorCode": "request.unsupported_media_type",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "resource.link_conflict": {
        "summary": "resource.link_conflict",
        "description": "The link cannot be made as asked: the vehicle already has a driver, the driver or device is linked to another vehicle, the two are in different organizations or fleets, or the registration number is taken in the target organization. Unassign the other link or change the target first.",
        "value": {
          "type": "https://connect.spillard.live/errors/409",
          "title": "Conflict",
          "status": 409,
          "detail": "The link cannot be made as asked: the vehicle already has a driver, the driver or device is linked to another vehicle, the two are in different organizations or fleets, or the registration number is taken in the target organization. Unassign the other link or change the target first.",
          "instance": "/v1/vehicles/7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041/drivers/c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945",
          "errorCode": "resource.link_conflict",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "server.not_implemented": {
        "summary": "server.not_implemented",
        "description": "The operation is not available on this host. Do not retry; quote `traceId` to Spillard support.",
        "value": {
          "type": "https://connect.spillard.live/errors/501",
          "title": "Not Implemented",
          "status": 501,
          "detail": "The operation is not available on this host. Do not retry; quote `traceId` to Spillard support.",
          "instance": "/v1/vehicles",
          "errorCode": "server.not_implemented",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "server.unexpected": {
        "summary": "server.unexpected",
        "description": "An unexpected error. Retry with backoff; quote `traceId` to Spillard support if it persists.",
        "value": {
          "type": "https://connect.spillard.live/errors/500",
          "title": "Internal Server Error",
          "status": 500,
          "detail": "An unexpected error. Retry with backoff; quote `traceId` to Spillard support if it persists.",
          "instance": "/v1/vehicles",
          "errorCode": "server.unexpected",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": true
        }
      },
      "validation.cursor_invalid": {
        "summary": "validation.cursor_invalid",
        "description": "The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page.",
        "value": {
          "type": "https://connect.spillard.live/errors/400",
          "title": "Bad Request",
          "status": 400,
          "detail": "The pagination cursor is not valid.",
          "instance": "/v1/vehicles",
          "errorCode": "validation.cursor_invalid",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false,
          "errors": {
            "cursor": [
              "The pagination cursor is not valid."
            ]
          }
        }
      },
      "validation.failed": {
        "summary": "validation.failed",
        "description": "A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again.",
        "value": {
          "type": "https://connect.spillard.live/errors/400",
          "title": "Bad Request",
          "status": 400,
          "detail": "One or more validation errors occurred.",
          "instance": "/v1/vehicles",
          "errorCode": "validation.failed",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false,
          "errors": {
            "limit": [
              "limit must be at least 1 (at most 1000; larger values are reduced to 1000)."
            ]
          }
        }
      },
      "validation.reference_not_found": {
        "summary": "validation.reference_not_found",
        "description": "An id in the body (for example `fleetId`) does not exist or is outside your client's access. Send an id your client may read.",
        "value": {
          "type": "https://connect.spillard.live/errors/422",
          "title": "Unprocessable Entity",
          "status": 422,
          "detail": "An id in the body (for example `fleetId`) does not exist or is outside your client's access. Send an id your client may read.",
          "instance": "/v1/drivers",
          "errorCode": "validation.reference_not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "vehicle.already_in_fleet": {
        "summary": "vehicle.already_in_fleet",
        "description": "The vehicle is in the target fleet already, so nothing would change. Choose another fleet.",
        "value": {
          "type": "https://connect.spillard.live/errors/409",
          "title": "Conflict",
          "status": 409,
          "detail": "The vehicle is in the target fleet already, so nothing would change. Choose another fleet.",
          "instance": "/v1/vehicles/7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041:move",
          "errorCode": "vehicle.already_in_fleet",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "vehicle.driver_fleet_mismatch": {
        "summary": "vehicle.driver_fleet_mismatch",
        "description": "The vehicle and the driver are in different fleets. Use a driver of the vehicle's fleet, or move the vehicle first.",
        "value": {
          "type": "https://connect.spillard.live/errors/422",
          "title": "Unprocessable Entity",
          "status": 422,
          "detail": "The vehicle and the driver are in different fleets. Use a driver of the vehicle's fleet, or move the vehicle first.",
          "instance": "/v1/vehicles/7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041/drivers/c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945",
          "errorCode": "vehicle.driver_fleet_mismatch",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "vehicle.not_found": {
        "summary": "vehicle.not_found",
        "description": "The vehicle does not exist or is outside your client's access. Check the id.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "The vehicle does not exist or is outside your client's access. Check the id.",
          "instance": "/v1/vehicles/7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
          "errorCode": "vehicle.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "webhook_delivery.not_found": {
        "summary": "webhook_delivery.not_found",
        "description": "No pending or failed delivery with this id belongs to the endpoint; delivered messages are not listed. Check the id in the delivery list.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "No pending or failed delivery with this id belongs to the endpoint; delivered messages are not listed. Check the id in the delivery list.",
          "instance": "/v1/webhook-endpoints/4b9e2d71-8c5a-4f3e-9d16-a7c0e5b2f849/deliveries/8e2d4a71-c63f-5b09-9d4e-1a7f6c2b8e35:replay",
          "errorCode": "webhook_delivery.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "webhook_delivery.not_replayable": {
        "summary": "webhook_delivery.not_replayable",
        "description": "The delivery cannot be replayed now: the endpoint does not receive its type any more, the message was not kept in full, or a delivery of the same message is pending. Check the endpoint settings, or wait for the pending delivery.",
        "value": {
          "type": "https://connect.spillard.live/errors/409",
          "title": "Conflict",
          "status": 409,
          "detail": "The delivery cannot be replayed now: the endpoint does not receive its type any more, the message was not kept in full, or a delivery of the same message is pending. Check the endpoint settings, or wait for the pending delivery.",
          "instance": "/v1/webhook-endpoints/4b9e2d71-8c5a-4f3e-9d16-a7c0e5b2f849/deliveries/8e2d4a71-c63f-5b09-9d4e-1a7f6c2b8e35:replay",
          "errorCode": "webhook_delivery.not_replayable",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "webhook_endpoint.conflict": {
        "summary": "webhook_endpoint.conflict",
        "description": "The endpoint name is taken, a webhook type is subscribed by another active endpoint, or the endpoint has no usable signing secret. Change the name or the types, or issue a secret with `:rotate-secret`.",
        "value": {
          "type": "https://connect.spillard.live/errors/409",
          "title": "Conflict",
          "status": 409,
          "detail": "The endpoint name is taken, a webhook type is subscribed by another active endpoint, or the endpoint has no usable signing secret. Change the name or the types, or issue a secret with `:rotate-secret`.",
          "instance": "/v1/webhook-endpoints",
          "errorCode": "webhook_endpoint.conflict",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "webhook_endpoint.limit_exceeded": {
        "summary": "webhook_endpoint.limit_exceeded",
        "description": "The organization has the maximum number of webhook endpoints (`endpointLimit` in `GET /v1/webhook-settings`) already. Delete an endpoint first.",
        "value": {
          "type": "https://connect.spillard.live/errors/409",
          "title": "Conflict",
          "status": 409,
          "detail": "The organization has the maximum number of webhook endpoints (`endpointLimit` in `GET /v1/webhook-settings`) already. Delete an endpoint first.",
          "instance": "/v1/webhook-endpoints",
          "errorCode": "webhook_endpoint.limit_exceeded",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      },
      "webhook_endpoint.not_found": {
        "summary": "webhook_endpoint.not_found",
        "description": "No webhook endpoint with this id belongs to the organization. Check the id.",
        "value": {
          "type": "https://connect.spillard.live/errors/404",
          "title": "Not Found",
          "status": 404,
          "detail": "No webhook endpoint with this id belongs to the organization. Check the id.",
          "instance": "/v1/webhook-endpoints/4b9e2d71-8c5a-4f3e-9d16-a7c0e5b2f849",
          "errorCode": "webhook_endpoint.not_found",
          "requestId": null,
          "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
          "retryable": false
        }
      }
    },
    "headers": {
      "RateLimit-Policy": {
        "description": "The client's quota: `\"client\";q=<units>;w=<window seconds>` (IETF RateLimit header fields). A request costs 1 unit or more (see Rate limits). Sent on responses to authenticated requests.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit": {
        "description": "What is left of the quota: `\"client\";r=<remaining units>;t=<seconds until the oldest request in the window stops counting>`. `r=0` on a 429.",
        "schema": {
          "type": "string"
        }
      }
    },
    "securitySchemes": {
      "OAuth2": {
        "type": "oauth2",
        "description": "OAuth2 client credentials at Spillard Identity: POST `grant_type=client_credentials` with your client id and secret to the token URL below. Without `scope` the token carries every scope of the client. Send the token as `Authorization: Bearer <token>`.",
        "flows": {
          "clientCredentials": {
            "tokenUrl": "https://identity.spillard.live/connect/token",
            "scopes": {
              "api-clients.read": "Read the organization's API clients.",
              "api-clients.write": "Create, change and delete API clients and rotate their secrets (scopes limited to your own).",
              "commands.read": "Read commands sent to devices.",
              "devices.read": "Read devices, device positions and the device catalog (models, protocols).",
              "devices.write": "Turn data from a device on or off; move a device to another fleet.",
              "drivers.read": "Read drivers.",
              "drivers.write": "Create, update and delete drivers.",
              "events.read": "Read device events, including per device and per journey.",
              "fleets.read": "Read fleets.",
              "fleets.write": "Create fleets.",
              "journeys.read": "Read journeys and their frames.",
              "media.read": "Read media requests and get download links for footage.",
              "media.write": "Request video clips from devices.",
              "organizations.read": "Read the organizations your client can access.",
              "vehicles.read": "Read vehicles and their latest positions.",
              "vehicles.write": "Create, update and delete vehicles; assign drivers and devices to vehicles.",
              "webhook-settings.read": "See whether webhook delivery is on for the organization.",
              "webhook-settings.write": "Turn webhook delivery on or off for the organization.",
              "webhooks.read": "Read webhook endpoints and their failed or retrying deliveries.",
              "webhooks.write": "Create, change, test and delete webhook endpoints; rotate signing secrets; replay deliveries."
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "OAuth2": [ ]
    }
  ],
  "tags": [
    {
      "name": "Status",
      "description": "Check that the API is up and which version serves your requests.",
      "x-displayName": "Service status"
    },
    {
      "name": "Context",
      "description": "See how the API sees your token: your client, its organizations and fleets, scopes and licenses.",
      "x-displayName": "Your access"
    },
    {
      "name": "Organizations",
      "description": "The organizations your client may read.",
      "x-displayName": "Organizations"
    },
    {
      "name": "Fleets",
      "description": "Fleets group the vehicles, drivers and devices of an organization.",
      "x-displayName": "Fleets"
    },
    {
      "name": "Vehicles",
      "description": "Vehicles, their latest positions, the drivers and devices assigned to them, and moves between fleets.\n\n**Connectivity.** The status comes from the time Spillard last received data from a device: `online` within 2 minutes, `offline` up to 72 hours, `sleep` after that or when the device never reported. A vehicle has the status of its most recently reporting device. The Spillard web app calls `sleep` Inactive. The device and vehicle lists filter by `connectivityStatus` and `lastReportedFrom`; `count=true` is not available with them. Vehicle positions carry the status too.",
      "x-displayName": "Vehicles"
    },
    {
      "name": "Drivers",
      "description": "Drivers of your organizations.",
      "x-displayName": "Drivers"
    },
    {
      "name": "Devices",
      "description": "Tracking and camera devices: details, latest position, events, and whether Spillard accepts their data.\n\n**Connectivity.** The status comes from the time Spillard last received data from a device: `online` within 2 minutes, `offline` up to 72 hours, `sleep` after that or when the device never reported. A vehicle has the status of its most recently reporting device. The Spillard web app calls `sleep` Inactive. The device and vehicle lists filter by `connectivityStatus` and `lastReportedFrom`; `count=true` is not available with them. Vehicle positions carry the status too.",
      "x-displayName": "Devices"
    },
    {
      "name": "DeviceCatalog",
      "description": "The device models and communication protocols Spillard supports.",
      "x-displayName": "Device catalog"
    },
    {
      "name": "Events",
      "description": "Events raised by devices (harsh driving, fatigue, panic button and more), and public links to share them.",
      "x-displayName": "Events"
    },
    {
      "name": "Journeys",
      "description": "Completed journeys with their positions and events.",
      "x-displayName": "Journeys"
    },
    {
      "name": "Commands",
      "description": "Commands sent to devices, such as video requests.",
      "x-displayName": "Device commands"
    },
    {
      "name": "Metrics",
      "description": "Daily figures per vehicle: human detection and the time the vehicle was driven.",
      "x-displayName": "Metrics"
    },
    {
      "name": "Media",
      "description": "Request a video clip from a device, follow the request, and download the files of events, commands and requests.",
      "x-displayName": "Video requests and media"
    },
    {
      "name": "ApiClients",
      "description": "The API clients of your organizations: create them, change their scopes, rotate their secrets. These operations need the `api-clients.read` or `api-clients.write` scope, which Spillard grants on request.",
      "x-displayName": "API clients"
    },
    {
      "name": "WebhookEndpoints",
      "description": "Your HTTPS endpoints for webhooks: subscribe them to webhook types (`eventTypes`), narrow event messages by device event type (`eventTypeFilters`), test them, and list and replay failed deliveries.",
      "x-displayName": "Webhook endpoints"
    },
    {
      "name": "WebhookSettings",
      "description": "The switch that turns webhook delivery on or off for your organization.",
      "x-displayName": "Webhook settings"
    }
  ],
  "externalDocs": {
    "description": "Getting started: credentials, the first token and call, every resource, and the webhook messages.",
    "url": "https://connect.spillard.live/"
  },
  "x-tagGroups": [
    {
      "name": "Getting started",
      "tags": [
        "Status",
        "Context"
      ]
    },
    {
      "name": "Fleet data",
      "tags": [
        "Organizations",
        "Fleets",
        "Vehicles",
        "Drivers",
        "Devices",
        "DeviceCatalog"
      ]
    },
    {
      "name": "Telematics",
      "tags": [
        "Events",
        "Journeys",
        "Metrics"
      ]
    },
    {
      "name": "Video",
      "tags": [
        "Media",
        "Commands"
      ]
    },
    {
      "name": "Webhooks",
      "tags": [
        "WebhookEndpoints",
        "WebhookSettings"
      ]
    },
    {
      "name": "Administration",
      "tags": [
        "ApiClients"
      ]
    }
  ],
  "x-error-codes": [
    {
      "code": "api_client.not_found",
      "status": 404,
      "description": "The API client does not exist or is outside your client's access. Check the id."
    },
    {
      "code": "audit_form.required",
      "status": 422,
      "description": "The fleet requires a video audit form for this action, which the API cannot submit. Do it in the Spillard web app."
    },
    {
      "code": "auth.fleet_restricted",
      "status": 403,
      "description": "Your client reads this organization for some of its fleets only, and the operation needs all of them (creating a fleet or an API client, managing webhooks). Use an organization your client reads in full."
    },
    {
      "code": "auth.scope_missing",
      "status": 403,
      "description": "The token lacks the scope the operation requires, or the request names a scope your client does not hold. Grant the scope to the API client and get a new token."
    },
    {
      "code": "auth.tenant_forbidden",
      "status": 403,
      "description": "The organization is outside your client's access, the request needs sub-organization access your client lacks, or the token's access list is damaged. Check `access` in `GET /v1/context`, or ask your Spillard account manager to change your client's access."
    },
    {
      "code": "auth.token_invalid",
      "status": 401,
      "description": "The bearer token is missing, malformed, expired or issued for another API. Get a new token."
    },
    {
      "code": "command.not_found",
      "status": 404,
      "description": "The command or media request does not exist or is outside your client's access. Check the id. The media request routes answer with this code too."
    },
    {
      "code": "concurrency.etag_mismatch",
      "status": 412,
      "description": "The resource changed since you read it: `If-Match` no longer matches its `ETag`. Read it again, then retry the update."
    },
    {
      "code": "data.retention_restricted",
      "status": 403,
      "description": "The data is older than the organization's data access allows (see `dataAccessDays` in `GET /v1/context`). Ask for newer data."
    },
    {
      "code": "dependency.timeout",
      "status": 504,
      "description": "A Spillard service did not answer in time. Retry with backoff."
    },
    {
      "code": "dependency.unavailable",
      "status": 503,
      "description": "A Spillard service is temporarily unavailable. Retry with backoff."
    },
    {
      "code": "device.command_not_supported",
      "status": 400,
      "description": "The device does not support the requested command. Choose a command the device supports."
    },
    {
      "code": "device.not_found",
      "status": 404,
      "description": "The device does not exist or is outside your client's access. Check the id or hardware id. On a vehicle route it can also mean the device is not fitted to that vehicle."
    },
    {
      "code": "device_model.not_found",
      "status": 404,
      "description": "No device model has this id. Check the id."
    },
    {
      "code": "device_protocol.not_found",
      "status": 404,
      "description": "No device protocol has this id. Check the id."
    },
    {
      "code": "driver.not_found",
      "status": 404,
      "description": "The driver does not exist or is outside your client's access. Check the id. On a vehicle route it can also mean the driver does not drive that vehicle."
    },
    {
      "code": "event.not_found",
      "status": 404,
      "description": "The event does not exist or is outside your client's access. Check the id."
    },
    {
      "code": "event.share_restricted",
      "status": 403,
      "description": "The event cannot be shared by link: its page would show data outside what your client may read. Do not share this event, or ask your Spillard account manager for wider access for your client."
    },
    {
      "code": "fleet.not_found",
      "status": 404,
      "description": "The fleet does not exist or is outside your client's access. Check the id."
    },
    {
      "code": "idempotency.in_progress",
      "status": 409,
      "description": "The first request with this `Idempotency-Key` is still being handled, or ended without a known result. `retryable` is false: wait a few seconds, then send the same request with the same key to get its result. After a server error on a media request the key stays held for 24 hours: check `GET /v1/commands` before you use a new key."
    },
    {
      "code": "idempotency.key_conflict",
      "status": 409,
      "description": "The `Idempotency-Key` was already used with another request body (or, for an API client, the client has changed since). Send the new request with a new key."
    },
    {
      "code": "journey.not_found",
      "status": 404,
      "description": "The journey does not exist or is outside your client's access. Check the id."
    },
    {
      "code": "organization.not_found",
      "status": 404,
      "description": "The organization does not exist or is outside your client's access. Check the id."
    },
    {
      "code": "precondition.required",
      "status": 428,
      "description": "A required header is missing: `If-Match` when you update a vehicle or a driver, `Idempotency-Key` when you create a media request. Send it and retry."
    },
    {
      "code": "rate.limit_exceeded",
      "status": 429,
      "description": "The API client sent more requests than its rate limit allows. Wait for `Retry-After` seconds, then retry."
    },
    {
      "code": "request.unsupported_media_type",
      "status": 415,
      "description": "The body is not JSON. Send it with `Content-Type: application/json`."
    },
    {
      "code": "resource.link_conflict",
      "status": 409,
      "description": "The link cannot be made as asked: the vehicle already has a driver, the driver or device is linked to another vehicle, the two are in different organizations or fleets, or the registration number is taken in the target organization. Unassign the other link or change the target first."
    },
    {
      "code": "server.not_implemented",
      "status": 501,
      "description": "The operation is not available on this host. Do not retry; quote `traceId` to Spillard support."
    },
    {
      "code": "server.unexpected",
      "status": 500,
      "description": "An unexpected error. Retry with backoff; quote `traceId` to Spillard support if it persists."
    },
    {
      "code": "validation.cursor_invalid",
      "status": 400,
      "description": "The `cursor` is malformed, was changed, or does not belong to this request (for example another `order`). Start again from the first page."
    },
    {
      "code": "validation.failed",
      "status": 400,
      "description": "A parameter or a field of the body is missing or invalid. `errors` names each field with its messages. Fix them and send the request again."
    },
    {
      "code": "validation.reference_not_found",
      "status": 422,
      "description": "An id in the body (for example `fleetId`) does not exist or is outside your client's access. Send an id your client may read."
    },
    {
      "code": "vehicle.already_in_fleet",
      "status": 409,
      "description": "The vehicle is in the target fleet already, so nothing would change. Choose another fleet."
    },
    {
      "code": "vehicle.driver_fleet_mismatch",
      "status": 422,
      "description": "The vehicle and the driver are in different fleets. Use a driver of the vehicle's fleet, or move the vehicle first."
    },
    {
      "code": "vehicle.not_found",
      "status": 404,
      "description": "The vehicle does not exist or is outside your client's access. Check the id."
    },
    {
      "code": "webhook_delivery.not_found",
      "status": 404,
      "description": "No pending or failed delivery with this id belongs to the endpoint; delivered messages are not listed. Check the id in the delivery list."
    },
    {
      "code": "webhook_delivery.not_replayable",
      "status": 409,
      "description": "The delivery cannot be replayed now: the endpoint does not receive its type any more, the message was not kept in full, or a delivery of the same message is pending. Check the endpoint settings, or wait for the pending delivery."
    },
    {
      "code": "webhook_endpoint.conflict",
      "status": 409,
      "description": "The endpoint name is taken, a webhook type is subscribed by another active endpoint, or the endpoint has no usable signing secret. Change the name or the types, or issue a secret with `:rotate-secret`."
    },
    {
      "code": "webhook_endpoint.limit_exceeded",
      "status": 409,
      "description": "The organization has the maximum number of webhook endpoints (`endpointLimit` in `GET /v1/webhook-settings`) already. Delete an endpoint first."
    },
    {
      "code": "webhook_endpoint.not_found",
      "status": 404,
      "description": "No webhook endpoint with this id belongs to the organization. Check the id."
    }
  ],
  "webhooks": {
    "spillard.alarm.raised.v1": {
      "post": {
        "summary": "spillard.alarm.raised.v1",
        "description": "A device raised an alarm that Spillard accepted. `kind` says what raised it; `eventId` is the event Spillard created from it, or null. That event is also delivered as a `spillard.event.raised.v1` message to the endpoints subscribed to that type.\n\nSpillard POSTs this message to every active endpoint subscribed to the type, while webhooks are switched on for your organization. Delivery is at least once and unordered: deduplicate on `webhook-id`. Verify `webhook-signature` before you trust the body. A failing delivery is retried with backoff for up to 24 hours, then it fails and is listed in the delivery log (`GET /v1/webhook-endpoints/{endpointId}/deliveries`), from where it can be replayed.",
        "operationId": "alarmRaisedWebhook",
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "description": "The message id, equal to the CloudEvents `id`. A retry carries the same id: deduplicate on it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "description": "When this attempt was sent, in Unix seconds. Part of the signed content. Every attempt, retry and replay carries a new one.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "description": "`v1,<base64 HMAC-SHA256>` of `{webhook-id}.{webhook-timestamp}.{body}` with the endpoint's signing secret (the base64 part after `whsec_`). During a secret rotation it carries one space-separated signature per active secret, the current one first; accept the message if any of them verifies.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "A CloudEvents 1.0 message in structured mode (`content-type: application/cloudevents+json; charset=utf-8`).",
          "content": {
            "application/cloudevents+json": {
              "schema": {
                "$ref": "#/components/schemas/AlarmRaisedMessage"
              },
              "example": {
                "specversion": "1.0",
                "id": "0b7c3e9a-51d4-5f28-a6c1-9e3d7f0b2a64",
                "type": "spillard.alarm.raised.v1",
                "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                "subject": "alarms/4d2f8a61-9c7e-4b15-83d0-a6e5f1c2b978",
                "time": "2026-09-29T07:12:55.000Z",
                "datacontenttype": "application/json",
                "data": {
                  "alarmId": "4d2f8a61-9c7e-4b15-83d0-a6e5f1c2b978",
                  "triggeredAt": "2026-09-29T07:12:55.000Z",
                  "kind": "io_input",
                  "input": 2,
                  "vendorKey": "ME41_ALARM_IO_ALARM2",
                  "location": {
                    "latitude": 51.53184,
                    "longitude": -0.262915
                  },
                  "speedKph": 0,
                  "headingDegrees": 90,
                  "eventId": null,
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                    "registration": "SL24 WHK"
                  },
                  "driver": {
                    "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                  },
                  "device": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer with any 2xx status within 10 seconds; the response body is ignored. A timeout, 408, 429 or 5xx is retried (a `Retry-After` of up to 6 hours replaces a shorter delay). Any other status, and a redirect, fails the delivery at once."
          }
        },
        "security": [ ]
      }
    },
    "spillard.event.raised.v1": {
      "post": {
        "summary": "spillard.event.raised.v1",
        "description": "A device raised an event. `eventTypes` holds its device event types as `family.name` tokens (for example `dsm.fatigue`); an endpoint can filter on them with `eventTypeFilters`. `links.self` is `/v1/events/{eventId}` and `links.media` is `/v1/events/{eventId}/media`.\n\nSpillard POSTs this message to every active endpoint subscribed to the type, while webhooks are switched on for your organization. Delivery is at least once and unordered: deduplicate on `webhook-id`. Verify `webhook-signature` before you trust the body. A failing delivery is retried with backoff for up to 24 hours, then it fails and is listed in the delivery log (`GET /v1/webhook-endpoints/{endpointId}/deliveries`), from where it can be replayed.",
        "operationId": "eventRaisedWebhook",
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "description": "The message id, equal to the CloudEvents `id`. A retry carries the same id: deduplicate on it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "description": "When this attempt was sent, in Unix seconds. Part of the signed content. Every attempt, retry and replay carries a new one.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "description": "`v1,<base64 HMAC-SHA256>` of `{webhook-id}.{webhook-timestamp}.{body}` with the endpoint's signing secret (the base64 part after `whsec_`). During a secret rotation it carries one space-separated signature per active secret, the current one first; accept the message if any of them verifies.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "A CloudEvents 1.0 message in structured mode (`content-type: application/cloudevents+json; charset=utf-8`).",
          "content": {
            "application/cloudevents+json": {
              "schema": {
                "$ref": "#/components/schemas/EventRaisedMessage"
              },
              "example": {
                "specversion": "1.0",
                "id": "8e2d4a71-c63f-5b09-9d4e-1a7f6c2b8e35",
                "type": "spillard.event.raised.v1",
                "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                "subject": "events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                "time": "2026-09-29T07:41:17.000Z",
                "datacontenttype": "application/json",
                "data": {
                  "eventId": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                  "triggeredAt": "2026-09-29T07:41:17.000Z",
                  "receivedAt": "2026-09-29T07:41:19.412Z",
                  "eventTypes": [
                    "driver_behaviour.harsh_braking"
                  ],
                  "classification": "medium",
                  "origin": "device",
                  "location": {
                    "latitude": 51.530112,
                    "longitude": -0.292731
                  },
                  "address": {
                    "label": "Western Avenue (A40), Ealing, London, United Kingdom",
                    "street": "Western Avenue",
                    "city": "London",
                    "postalCode": null,
                    "countryCode": "GBR"
                  },
                  "speedKph": 54,
                  "maxSpeedKph": 57,
                  "speedLimitKph": 64,
                  "headingDegrees": 263,
                  "mediaExpected": true,
                  "firmwareVersion": "T250922.01",
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                    "registration": "SL24 WHK"
                  },
                  "driver": {
                    "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                  },
                  "device": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "links": {
                    "self": "/v1/events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                    "media": "/v1/events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53/media"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer with any 2xx status within 10 seconds; the response body is ignored. A timeout, 408, 429 or 5xx is retried (a `Retry-After` of up to 6 hours replaces a shorter delay). Any other status, and a redirect, fails the delivery at once."
          }
        },
        "security": [ ]
      }
    },
    "spillard.journey.completed.v1": {
      "post": {
        "summary": "spillard.journey.completed.v1",
        "description": "A journey was completed or reprocessed. The latest `revision` wins; delete the ids in `replaces`. No `driver` in v1: Spillard does not know who drove a journey.\n\nSpillard POSTs this message to every active endpoint subscribed to the type, while webhooks are switched on for your organization. Delivery is at least once and unordered: deduplicate on `webhook-id`. Verify `webhook-signature` before you trust the body. A failing delivery is retried with backoff for up to 24 hours, then it fails and is listed in the delivery log (`GET /v1/webhook-endpoints/{endpointId}/deliveries`), from where it can be replayed.",
        "operationId": "journeyCompletedWebhook",
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "description": "The message id, equal to the CloudEvents `id`. A retry carries the same id: deduplicate on it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "description": "When this attempt was sent, in Unix seconds. Part of the signed content. Every attempt, retry and replay carries a new one.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "description": "`v1,<base64 HMAC-SHA256>` of `{webhook-id}.{webhook-timestamp}.{body}` with the endpoint's signing secret (the base64 part after `whsec_`). During a secret rotation it carries one space-separated signature per active secret, the current one first; accept the message if any of them verifies.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "A CloudEvents 1.0 message in structured mode (`content-type: application/cloudevents+json; charset=utf-8`).",
          "content": {
            "application/cloudevents+json": {
              "schema": {
                "$ref": "#/components/schemas/JourneyCompletedMessage"
              },
              "example": {
                "specversion": "1.0",
                "id": "5a9f1c3e-7d2b-5e84-b6a0-c3e8d1f7a492",
                "type": "spillard.journey.completed.v1",
                "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                "subject": "journeys/20260929T065804000Z009a2c41f7",
                "time": "2026-09-29T08:02:40.000Z",
                "datacontenttype": "application/json",
                "data": {
                  "journeyId": "20260929T065804000Z009a2c41f7",
                  "revision": 1,
                  "replaces": [ ],
                  "startedAt": "2026-09-29T06:58:04.000Z",
                  "endedAt": "2026-09-29T08:02:40.000Z",
                  "start": {
                    "location": {
                      "latitude": 51.532704,
                      "longitude": -0.268213
                    },
                    "address": {
                      "label": "Coronation Road, Park Royal, London, United Kingdom",
                      "street": "Coronation Road",
                      "city": "London",
                      "postalCode": null,
                      "countryCode": "GBR"
                    }
                  },
                  "end": {
                    "location": {
                      "latitude": 51.481022,
                      "longitude": -0.429587
                    },
                    "address": {
                      "label": "Bath Road, Harlington, Hayes, United Kingdom",
                      "street": "Bath Road",
                      "city": "Hayes",
                      "postalCode": null,
                      "countryCode": "GBR"
                    }
                  },
                  "distanceMeters": 21480,
                  "durationSeconds": 3876,
                  "movingSeconds": 3102,
                  "idleSeconds": 774,
                  "ignitionOnSeconds": 3876,
                  "maxSpeedKph": 81,
                  "speedingCount": 2,
                  "gpsCoverage": "full",
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                    "registration": "SL24 WHK"
                  },
                  "device": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer with any 2xx status within 10 seconds; the response body is ignored. A timeout, 408, 429 or 5xx is retried (a `Retry-After` of up to 6 hours replaces a shorter delay). Any other status, and a redirect, fails the delivery at once."
          }
        },
        "security": [ ]
      }
    },
    "spillard.telemetry.live.v1": {
      "post": {
        "summary": "spillard.telemetry.live.v1",
        "description": "The latest positions of the organization's vehicles, batched every 5 seconds.\n\nSpillard POSTs this message to every active endpoint subscribed to the type, while webhooks are switched on for your organization. Delivery is at least once and unordered: deduplicate on `webhook-id`. Verify `webhook-signature` before you trust the body. A failing batch is retried for 60 seconds and then dropped; it never appears in the delivery log and cannot be replayed. Positions are batched per organization: a batch is sent 5 seconds after its first position, with at most 500 positions.",
        "operationId": "telemetryLiveWebhook",
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "description": "The message id, equal to the CloudEvents `id`. A retry carries the same id: deduplicate on it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "description": "When this attempt was sent, in Unix seconds. Part of the signed content. Every attempt, retry and replay carries a new one.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "description": "`v1,<base64 HMAC-SHA256>` of `{webhook-id}.{webhook-timestamp}.{body}` with the endpoint's signing secret (the base64 part after `whsec_`). During a secret rotation it carries one space-separated signature per active secret, the current one first; accept the message if any of them verifies.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "A CloudEvents 1.0 message in structured mode (`content-type: application/cloudevents+json; charset=utf-8`).",
          "content": {
            "application/cloudevents+json": {
              "schema": {
                "$ref": "#/components/schemas/TelemetryLiveMessage"
              },
              "example": {
                "specversion": "1.0",
                "id": "d3a7e1f9-4b6c-5d82-a9e3-6f1c0b8d4e27",
                "type": "spillard.telemetry.live.v1",
                "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                "time": "2026-09-29T07:41:18.000Z",
                "datacontenttype": "application/json",
                "data": {
                  "positions": [
                    {
                      "recordedAt": "2026-09-29T07:41:18.000Z",
                      "location": {
                        "latitude": 51.530103,
                        "longitude": -0.292884
                      },
                      "speedKph": 38,
                      "headingDegrees": 263,
                      "ignitionOn": true,
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                        "registration": "SL24 WHK"
                      },
                      "driver": {
                        "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                      },
                      "device": {
                        "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                        "hardwareId": "009a2c41f7"
                      }
                    },
                    {
                      "recordedAt": "2026-09-29T07:41:16.000Z",
                      "location": {
                        "latitude": 51.532655,
                        "longitude": -0.268391
                      },
                      "speedKph": 0,
                      "headingDegrees": 182,
                      "ignitionOn": false,
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "5d8c2b7e-1a4f-4e93-8b6d-0f3a9c7e2d51",
                        "registration": "SL24 WHN"
                      },
                      "driver": null,
                      "device": {
                        "id": "2c7e9a14-58b3-4f06-9d21-a4e8f3b6c051",
                        "hardwareId": "009a2c4388"
                      }
                    }
                  ]
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer with any 2xx status within 10 seconds; the response body is ignored. A timeout, 408, 429 or 5xx is retried (a `Retry-After` of up to 6 hours replaces a shorter delay). Any other status, and a redirect, fails the delivery at once."
          }
        },
        "security": [ ]
      }
    },
    "spillard.telemetry.history.v1": {
      "post": {
        "summary": "spillard.telemetry.history.v1",
        "description": "Recorded telemetry tracks of the organization's devices, batched, late uploads included. Tracks carry no `driver` in v1.\n\nSpillard POSTs this message to every active endpoint subscribed to the type, while webhooks are switched on for your organization. Delivery is at least once and unordered: deduplicate on `webhook-id`. Verify `webhook-signature` before you trust the body. A failing delivery is retried with backoff for up to 72 hours, then it fails and is listed in the delivery log (`GET /v1/webhook-endpoints/{endpointId}/deliveries`). Tracks are batched per organization: a batch is sent 30 seconds after its first track, or earlier when it reaches 256 KB.",
        "operationId": "telemetryHistoryWebhook",
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "description": "The message id, equal to the CloudEvents `id`. A retry carries the same id: deduplicate on it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "description": "When this attempt was sent, in Unix seconds. Part of the signed content. Every attempt, retry and replay carries a new one.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "description": "`v1,<base64 HMAC-SHA256>` of `{webhook-id}.{webhook-timestamp}.{body}` with the endpoint's signing secret (the base64 part after `whsec_`). During a secret rotation it carries one space-separated signature per active secret, the current one first; accept the message if any of them verifies.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "A CloudEvents 1.0 message in structured mode (`content-type: application/cloudevents+json; charset=utf-8`).",
          "content": {
            "application/cloudevents+json": {
              "schema": {
                "$ref": "#/components/schemas/TelemetryHistoryMessage"
              },
              "example": {
                "specversion": "1.0",
                "id": "71c4e8b2-3d9f-5a16-b8e7-4a2d6c9f0e53",
                "type": "spillard.telemetry.history.v1",
                "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                "time": "2026-09-29T07:41:19.000Z",
                "datacontenttype": "application/json",
                "data": {
                  "tracks": [
                    {
                      "windowStartAt": "2026-09-29T07:41:15.000Z",
                      "windowEndAt": "2026-09-29T07:41:19.000Z",
                      "backfill": false,
                      "frameCount": 5,
                      "frames": [
                        {
                          "recordedAt": "2026-09-29T07:41:15.000Z",
                          "location": {
                            "latitude": 51.53014,
                            "longitude": -0.292299
                          },
                          "speedKph": 57,
                          "headingDegrees": 263,
                          "gpsValid": true,
                          "ignitionOn": true
                        },
                        {
                          "recordedAt": "2026-09-29T07:41:16.000Z",
                          "location": {
                            "latitude": 51.530126,
                            "longitude": -0.292526
                          },
                          "speedKph": 56,
                          "headingDegrees": 263,
                          "gpsValid": true,
                          "ignitionOn": true
                        },
                        {
                          "recordedAt": "2026-09-29T07:41:17.000Z",
                          "location": {
                            "latitude": 51.530112,
                            "longitude": -0.292731
                          },
                          "speedKph": 54,
                          "headingDegrees": 263,
                          "gpsValid": true,
                          "ignitionOn": true
                        },
                        {
                          "recordedAt": "2026-09-29T07:41:18.000Z",
                          "location": {
                            "latitude": 51.530103,
                            "longitude": -0.292884
                          },
                          "speedKph": 38,
                          "headingDegrees": 263,
                          "gpsValid": true,
                          "ignitionOn": true
                        },
                        {
                          "recordedAt": "2026-09-29T07:41:19.000Z",
                          "location": {
                            "latitude": 51.530097,
                            "longitude": -0.29298
                          },
                          "speedKph": 24,
                          "headingDegrees": 263,
                          "gpsValid": true,
                          "ignitionOn": true
                        }
                      ],
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                        "registration": "SL24 WHK"
                      },
                      "device": {
                        "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                        "hardwareId": "009a2c41f7"
                      }
                    },
                    {
                      "windowStartAt": "2026-09-29T05:12:00.000Z",
                      "windowEndAt": "2026-09-29T05:12:02.000Z",
                      "backfill": true,
                      "frameCount": 3,
                      "frames": [
                        {
                          "recordedAt": "2026-09-29T05:12:00.000Z",
                          "location": {
                            "latitude": 51.509812,
                            "longitude": -0.378655
                          },
                          "speedKph": 0,
                          "headingDegrees": 104,
                          "gpsValid": true,
                          "ignitionOn": false
                        },
                        {
                          "recordedAt": "2026-09-29T05:12:01.000Z",
                          "location": {
                            "latitude": 51.509812,
                            "longitude": -0.378655
                          },
                          "speedKph": 0,
                          "headingDegrees": 104,
                          "gpsValid": true,
                          "ignitionOn": true
                        },
                        {
                          "recordedAt": "2026-09-29T05:12:02.000Z",
                          "location": {
                            "latitude": 51.509808,
                            "longitude": -0.378631
                          },
                          "speedKph": 6,
                          "headingDegrees": 104,
                          "gpsValid": true,
                          "ignitionOn": true
                        }
                      ],
                      "organization": {
                        "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                      },
                      "fleet": {
                        "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                      },
                      "vehicle": {
                        "id": "e2b6f9c4-7d31-4a8e-9c52-8f1d0a6b3e97",
                        "registration": "SL73 KPE"
                      },
                      "device": {
                        "id": "b84e2c6a-91f3-4d70-a5c8-3e6f0d9b2741",
                        "hardwareId": "009a2c45d2"
                      }
                    }
                  ]
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer with any 2xx status within 10 seconds; the response body is ignored. A timeout, 408, 429 or 5xx is retried (a `Retry-After` of up to 6 hours replaces a shorter delay). Any other status, and a redirect, fails the delivery at once."
          }
        },
        "security": [ ]
      }
    },
    "spillard.media.request.completed.v1": {
      "post": {
        "summary": "spillard.media.request.completed.v1",
        "description": "A video request ended with `outcome` completed, failed or no_data. Only `completed` is reliable today; `failed` and `no_data` are in future development, do not rely on them. Poll `GET /v1/media-requests/{mediaRequestId}` until `state` is final: a request that ends without an answer from the device may send no message. `links.media` points to `/v1/media-requests/{mediaRequestId}/media`.\n\nSpillard POSTs this message to every active endpoint subscribed to the type, while webhooks are switched on for your organization. Delivery is at least once and unordered: deduplicate on `webhook-id`. Verify `webhook-signature` before you trust the body. A failing delivery is retried with backoff for up to 24 hours, then it fails and is listed in the delivery log (`GET /v1/webhook-endpoints/{endpointId}/deliveries`), from where it can be replayed. It reports video requests. Only `completed` is reliable today; `failed` and `no_data` are in future development, do not rely on them. Poll `GET /v1/media-requests/{mediaRequestId}` until `state` is final; a request that ends without an answer from the device may send no message.",
        "operationId": "mediaRequestCompletedWebhook",
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "description": "The message id, equal to the CloudEvents `id`. A retry carries the same id: deduplicate on it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "description": "When this attempt was sent, in Unix seconds. Part of the signed content. Every attempt, retry and replay carries a new one.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "description": "`v1,<base64 HMAC-SHA256>` of `{webhook-id}.{webhook-timestamp}.{body}` with the endpoint's signing secret (the base64 part after `whsec_`). During a secret rotation it carries one space-separated signature per active secret, the current one first; accept the message if any of them verifies.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "A CloudEvents 1.0 message in structured mode (`content-type: application/cloudevents+json; charset=utf-8`).",
          "content": {
            "application/cloudevents+json": {
              "schema": {
                "$ref": "#/components/schemas/MediaRequestCompletedMessage"
              },
              "example": {
                "specversion": "1.0",
                "id": "3c1f9e7d-8a4b-5f26-b0d9-e2c6a4f8b175",
                "type": "spillard.media.request.completed.v1",
                "source": "/organizations/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14",
                "subject": "mediaRequests/9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683",
                "time": "2026-09-29T08:07:48.000Z",
                "datacontenttype": "application/json",
                "data": {
                  "mediaRequestId": "9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683",
                  "trigger": "manual",
                  "externalId": "INS-CLM-2026-004417",
                  "outcome": "completed",
                  "requestedAt": "2026-09-29T08:05:11.000Z",
                  "completedAt": "2026-09-29T08:07:48.000Z",
                  "request": {
                    "startAt": "2026-09-29T07:41:02.000Z",
                    "durationSeconds": 30,
                    "channels": [
                      1,
                      2
                    ],
                    "overlay": true
                  },
                  "eventId": "e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53",
                  "organization": {
                    "id": "3f6c2a1e-8b4d-4c7a-9e21-5d0b7a3c9f14"
                  },
                  "fleet": {
                    "id": "a2d91c6f-3e57-4b08-8f1a-6c4e2b9d7a35"
                  },
                  "vehicle": {
                    "id": "7b1e4f92-0c3a-4d85-b6e7-19f2a8c5d041",
                    "registration": "SL24 WHK"
                  },
                  "driver": {
                    "id": "c84d2e17-5f9b-4a63-a0d8-3e7b1c6f2945"
                  },
                  "device": {
                    "id": "3a7c1e90-4d2b-4f6a-8e15-c0b9d2f47a63",
                    "hardwareId": "009a2c41f7"
                  },
                  "links": {
                    "media": "/v1/media-requests/9e4a7c21-6d8f-4b3e-a150-f2c9e7d4b683/media",
                    "event": "/v1/events/e5c7a912-4b3d-4f86-9a2e-0d1c8b6f7e53"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "description": "Received. Answer with any 2xx status within 10 seconds; the response body is ignored. A timeout, 408, 429 or 5xx is retried (a `Retry-After` of up to 6 hours replaces a shorter delay). Any other status, and a redirect, fails the delivery at once."
          }
        },
        "security": [ ]
      }
    }
  }
}