{
  "openapi": "3.1.0",
  "info": {
    "title": "Trackingen API",
    "version": "1",
    "description": "The key routes of the Trackingen API, the routes an API key may call. Send an API key, or a signed-in person's token, in the Authorization header as a bearer token; a key reaches only these routes, and only while its organization holds the API package. Every answer is a JSON object with the result in data. A list comes in pages and ends (keyset pagination): each page has page.next, a cursor you send back unchanged (an opaque cursor) and never build or read, and you ask again with after set to it for the next page. On the last page page.next is null, and the list is done. limit says how many come at most on a page. A copy never ends: it gives what was received after its cursor, in the order it was received and once it is a minute old, so a late report is never skipped. page.next is always where to go on from, so send it as after next time, even when the page was empty. page.more says whether to ask again at once: true while more is waiting, false once you are up to date, then ask again a minute or longer later. Without after a copy starts 14 days back. Starting it with from at a moment within the last 14 days counts against nothing; further back, within your kept data, it counts against 5 starts a day for each key or person, both copies together. An item may come more than once (at-least-once delivery), with more in it, so keep each by its deviceId and recordedAt. A refusal has its HTTP status and an error with a code that stays the same, a message for people and, for some codes, details. Every moment is in UTC and written in ISO 8601, such as 2026-11-04T08:00:00.000Z. Every route has its limit (rate limiting): a route counts your requests in a window, and a copy counts the items it goes through instead. Past the limit a route answers 429 with Retry-After, the seconds to wait. A route that changes something takes its body as JSON, sent with Content-Type: application/json. A route that makes something answers 201 with what it made, and takes an Idempotency-Key header of 8 to 64 letters, digits, dashes or underscores: when an answer is lost, send the same request with the same key, and for a day you get the first answer back, marked Idempotent-Replayed: true, instead of a second record. The same key with a different body answers IDEMPOTENCY_KEY_REUSED. A change sets the fields it names to what you send, so sending it again changes nothing more, and a removal answers 204, then 404 once the record is gone. Every change counts against its own limit for each key or person. Version 1 keeps its word: an answer may gain fields and a list of values may grow, but nothing is renamed or removed. Skip what you do not know, a field as much as a feed entry of a kind you have not seen.",
    "contact": {
      "name": "Trackingen",
      "url": "https://trackingen.com/en#contact"
    },
    "license": {
      "name": "Trackingen privacy notice",
      "url": "https://trackingen.com/en/privacy"
    }
  },
  "externalDocs": {
    "description": "Trackingen API docs",
    "url": "https://trackingen.com/en/api"
  },
  "servers": [
    {
      "url": "https://trackingen.com"
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "tags": [
    {
      "name": "Vehicles and positions",
      "description": "Your vehicles, where each one is now, its track over a period, a copy of every position as it arrives, and the trackers it has had."
    },
    {
      "name": "Trackers",
      "description": "The trackers the platform assigned to you, the vehicle each one is in, how each one is doing and what is wrong with it now."
    },
    {
      "name": "Drivers",
      "description": "The people who drive your vehicles, with their phone and licence, for routes. They come with the route planning package."
    },
    {
      "name": "Vehicle data",
      "description": "What your vehicles read, newest and over time, the signals each one gives, and its replaced ECUs."
    },
    {
      "name": "Fuel",
      "description": "Each vehicle's fuel and AdBlue tanks, and the drops and refuels the platform detects in them. They come with the fuel watch package."
    },
    {
      "name": "Drives and reports",
      "description": "Each vehicle's drives with their figures and score, its harsh moves, and your fleet's reports over a period."
    },
    {
      "name": "Trouble codes",
      "description": "The trouble codes your vehicles report, with their meaning, and how many are still open on each vehicle."
    },
    {
      "name": "Geofences",
      "description": "The places you draw on the map, whose entries and exits are told in the feed, how many you may keep, and each vehicle's stays in them."
    },
    {
      "name": "Alerts",
      "description": "The rules that raise an alert when a vehicle's reading passes a threshold, the signals a rule may watch, and the alerts raised."
    },
    {
      "name": "The feed",
      "description": "What happened in your fleet and what changed in your vehicles, devices and package, in one order."
    }
  ],
  "paths": {
    "/api/v1/alert-episodes": {
      "get": {
        "operationId": "listAlertEpisodes",
        "tags": [
          "Alerts"
        ],
        "summary": "Alerts raised",
        "description": "Each time a vehicle's reading passed an alert rule's threshold, newest first, with how far it went and for how long, and where it began to a key with position:read",
        "x-permissions": [
          "alert:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "description": "How many come at most on one page"
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "The page.next of the page before, to ask for the next page; send it as it came (an opaque cursor)"
          },
          {
            "name": "vehicleId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only this vehicle's alerts, by its id"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AlertEpisode"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/ListPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000301",
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "plate": "34 API 001",
                      "name": "Engine running hot",
                      "signal": "coolant-temperature",
                      "comparison": "above",
                      "threshold": 105,
                      "unit": "°C",
                      "peak": 112.5,
                      "startedAt": "2026-11-04T08:41:12.000Z",
                      "durationSeconds": 95,
                      "place": {
                        "latitude": 41.068,
                        "longitude": 29.011
                      }
                    }
                  ],
                  "page": {
                    "next": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/alert-rules": {
      "get": {
        "operationId": "listAlertRules",
        "tags": [
          "Alerts"
        ],
        "summary": "Your alert rules",
        "description": "Your alert rules, on and off, and every signal a rule may watch with the thresholds it takes and whether your package lets a rule watch it now",
        "x-permissions": [
          "alert:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AlertRules"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "rules": [
                      {
                        "id": "0199b000-0000-7000-8000-000000000201",
                        "name": "Engine running hot",
                        "signal": "coolant-temperature",
                        "comparison": "above",
                        "threshold": 105,
                        "unit": "°C",
                        "holdSeconds": 60,
                        "on": true
                      },
                      {
                        "id": "0199b000-0000-7000-8000-000000000202",
                        "name": "Battery running low",
                        "signal": "battery-low",
                        "comparison": "below",
                        "threshold": null,
                        "unit": "V",
                        "holdSeconds": 300,
                        "on": false
                      }
                    ],
                    "signals": [
                      {
                        "signal": "coolant-temperature",
                        "unit": "°C",
                        "threshold": {
                          "by": "customer",
                          "min": 40,
                          "max": 130,
                          "step": 1
                        },
                        "usable": true
                      },
                      {
                        "signal": "battery-low",
                        "unit": "V",
                        "threshold": {
                          "by": "platform",
                          "comparison": "below"
                        },
                        "usable": true
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createAlertRule",
        "tags": [
          "Alerts"
        ],
        "summary": "Add an alert rule",
        "description": "Adds a rule, on from the start: a vehicle whose signal stays past the threshold for the hold time raises an alert, told to your people and in the feed as vehicle-alert. Adding and changing alert rules also count together against 100 a day for your whole organization.",
        "x-permissions": [
          "alert:configure"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 60,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{8,64}$"
            },
            "description": "Your own key for this request, 8 to 64 letters, digits, dashes or underscores. Send the same key when you send the request again after a lost answer, and the first answer comes back instead of a second record"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "A name for the rule, plain text, shown with each alert"
                  },
                  "signal": {
                    "anyOf": [
                      {
                        "type": "string",
                        "const": "battery-low"
                      },
                      {
                        "type": "string",
                        "pattern": "^[a-z\\d-]{1,32}$"
                      }
                    ],
                    "description": "The signal the rule watches: one of the signals the alert rules list names, or battery-low"
                  },
                  "comparison": {
                    "type": "string",
                    "enum": [
                      "above",
                      "below"
                    ],
                    "description": "above or below: on which side of the threshold an alert is"
                  },
                  "threshold": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "The value the signal must pass, within its range and in its steps as the alert rules list gives them; null for battery-low, whose threshold the platform sets"
                  },
                  "holdSeconds": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 3600,
                    "description": "How long, in whole seconds, the signal stays past the threshold before the alert is raised; 0 raises it at once"
                  }
                },
                "required": [
                  "name",
                  "signal",
                  "comparison",
                  "threshold",
                  "holdSeconds"
                ],
                "additionalProperties": false
              },
              "example": {
                "name": "Engine running hot",
                "signal": "coolant-temperature",
                "comparison": "above",
                "threshold": 105,
                "holdSeconds": 60
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "What was made",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AlertRule"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "id": "0199b000-0000-7000-8000-000000000201",
                    "name": "Engine running hot",
                    "signal": "coolant-temperature",
                    "comparison": "above",
                    "threshold": 105,
                    "unit": "°C",
                    "holdSeconds": 60,
                    "on": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "ALERT_RULE_LIMIT, CONFLICT, IDEMPOTENCY_KEY_REUSED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "ALERT_RULE_LIMIT": {
                    "summary": "ALERT_RULE_LIMIT",
                    "value": {
                      "error": {
                        "code": "ALERT_RULE_LIMIT",
                        "message": "As many alert rules as may be on at once are on"
                      }
                    }
                  },
                  "CONFLICT": {
                    "summary": "CONFLICT",
                    "value": {
                      "error": {
                        "code": "CONFLICT",
                        "message": "The request conflicts with the present state"
                      }
                    }
                  },
                  "IDEMPOTENCY_KEY_REUSED": {
                    "summary": "IDEMPOTENCY_KEY_REUSED",
                    "value": {
                      "error": {
                        "code": "IDEMPOTENCY_KEY_REUSED",
                        "message": "This idempotency key was used before for a different request"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/alert-rules/{ruleId}": {
      "patch": {
        "operationId": "updateAlertRule",
        "tags": [
          "Alerts"
        ],
        "summary": "Change an alert rule",
        "description": "Changes the fields you send and keeps the rest, and turns the rule on or off. Adding and changing alert rules also count together against 100 a day for your whole organization.",
        "x-permissions": [
          "alert:configure",
          "alert:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 60,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "ruleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The rule's id, from your alert rules"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "A name for the rule, plain text, shown with each alert"
                  },
                  "signal": {
                    "anyOf": [
                      {
                        "type": "string",
                        "const": "battery-low"
                      },
                      {
                        "type": "string",
                        "pattern": "^[a-z\\d-]{1,32}$"
                      }
                    ],
                    "description": "The signal the rule watches: one of the signals the alert rules list names, or battery-low"
                  },
                  "comparison": {
                    "type": "string",
                    "enum": [
                      "above",
                      "below"
                    ],
                    "description": "above or below: on which side of the threshold an alert is"
                  },
                  "threshold": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "The value the signal must pass, within its range and in its steps as the alert rules list gives them; null for battery-low, whose threshold the platform sets"
                  },
                  "holdSeconds": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 3600,
                    "description": "How long, in whole seconds, the signal stays past the threshold before the alert is raised; 0 raises it at once"
                  },
                  "on": {
                    "type": "boolean",
                    "description": "Whether the rule raises alerts; past the rules that may be on at once, turning one on answers ALERT_RULE_LIMIT"
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "threshold": 110,
                "on": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AlertRule"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "id": "0199b000-0000-7000-8000-000000000201",
                    "name": "Engine running hot",
                    "signal": "coolant-temperature",
                    "comparison": "above",
                    "threshold": 110,
                    "unit": "°C",
                    "holdSeconds": 60,
                    "on": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "ALERT_RULE_LIMIT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "ALERT_RULE_LIMIT": {
                    "summary": "ALERT_RULE_LIMIT",
                    "value": {
                      "error": {
                        "code": "ALERT_RULE_LIMIT",
                        "message": "As many alert rules as may be on at once are on"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteAlertRule",
        "tags": [
          "Alerts"
        ],
        "summary": "Remove an alert rule",
        "description": "Removes a rule; the alerts it raised stay in the alert history",
        "x-permissions": [
          "alert:configure"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 60,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "ruleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The rule's id, from your alert rules"
          }
        ],
        "responses": {
          "204": {
            "description": "Done; the answer has no body"
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/devices": {
      "get": {
        "operationId": "listDevices",
        "tags": [
          "Trackers"
        ],
        "summary": "Your trackers",
        "description": "The trackers the platform assigned to you, with their serial number and the vehicle each one is in now",
        "x-permissions": [
          "device:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "description": "How many come at most on one page"
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "The page.next of the page before, to ask for the next page; send it as it came (an opaque cursor)"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Device"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/ListPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000401",
                      "serial": "356938035643809",
                      "label": "Van tracker",
                      "status": "active",
                      "vehicleId": "0199b000-0000-7000-8000-000000000001"
                    },
                    {
                      "id": "0199b000-0000-7000-8000-000000000402",
                      "serial": "356938035643817",
                      "label": null,
                      "status": "active",
                      "vehicleId": null
                    }
                  ],
                  "page": {
                    "next": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/devices/{deviceId}/health": {
      "get": {
        "operationId": "getDeviceHealth",
        "tags": [
          "Trackers"
        ],
        "summary": "How a tracker is doing",
        "description": "What the tracker last told about itself: its mobile signal, the vehicle's supply, its satellite position and how long it has been running. Every field is null until it first tells",
        "x-permissions": [
          "device:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "deviceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The tracker's id, from your trackers"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DeviceHealth"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "signalDbm": -71,
                    "supplyMillivolts": 12600,
                    "gnss": "fixed",
                    "lastFixAt": "2026-11-04T08:39:40.000Z",
                    "uptimeSeconds": 5400,
                    "healthReportedAt": "2026-11-04T08:40:00.000Z",
                    "lastReportAt": "2026-11-04T08:41:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/devices/{deviceId}/health-alerts": {
      "get": {
        "operationId": "listDeviceHealthAlerts",
        "tags": [
          "Trackers"
        ],
        "summary": "A tracker's open health alerts",
        "description": "What is wrong with the tracker now, such as having gone silent, each with since when; empty when nothing is",
        "x-permissions": [
          "device:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "deviceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The tracker's id, from your trackers"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DeviceHealthAlert"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000a01",
                      "condition": "silent",
                      "since": "2026-11-03T18:20:00.000Z",
                      "startedAt": "2026-11-04T18:20:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/drivers": {
      "get": {
        "operationId": "listDrivers",
        "tags": [
          "Drivers"
        ],
        "summary": "Your drivers",
        "description": "Your drivers, active and inactive, with their phone and licence",
        "x-permissions": [
          "driver:read"
        ],
        "x-feature": "routes",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "description": "How many come at most on one page"
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "The page.next of the page before, to ask for the next page; send it as it came (an opaque cursor)"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Driver"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/ListPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000601",
                      "name": "Ayşe Yılmaz",
                      "phone": "+905321234567",
                      "licenseNumber": "B1234567",
                      "status": "active",
                      "userId": null
                    },
                    {
                      "id": "0199b000-0000-7000-8000-000000000602",
                      "name": "Mehmet Demir",
                      "phone": null,
                      "licenseNumber": null,
                      "status": "inactive",
                      "userId": "0199b000-0000-7000-8000-000000000901"
                    }
                  ],
                  "page": {
                    "next": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createDriver",
        "tags": [
          "Drivers"
        ],
        "summary": "Add a driver",
        "description": "Adds a driver, active from the start, whom your routes can be given to",
        "x-permissions": [
          "driver:create"
        ],
        "x-feature": "routes",
        "x-rate-limit": {
          "attempts": 60,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{8,64}$"
            },
            "description": "Your own key for this request, 8 to 64 letters, digits, dashes or underscores. Send the same key when you send the request again after a lost answer, and the first answer comes back instead of a second record"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 240,
                    "description": "The driver's full name, plain text"
                  },
                  "phone": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 40,
                    "default": null,
                    "description": "A phone number of 7 to 15 digits, with + and the country code in front or without; spaces, brackets, dots and dashes are dropped. null for none"
                  },
                  "licenseNumber": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 64,
                    "default": null,
                    "description": "The driving licence number, kept in capitals; null for none"
                  },
                  "userId": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid",
                    "default": null,
                    "description": "The id of the person's account the driver signs in with to see their routes. Only a signed-in person links or unlinks an account: a key leaves it out, or sends it unchanged, and otherwise gets PERSON_REQUIRED"
                  }
                },
                "required": [
                  "name"
                ],
                "additionalProperties": false
              },
              "example": {
                "name": "Ayşe Yılmaz",
                "phone": "+905321234567",
                "licenseNumber": "B1234567"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "What was made",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Driver"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "id": "0199b000-0000-7000-8000-000000000601",
                    "name": "Ayşe Yılmaz",
                    "phone": "+905321234567",
                    "licenseNumber": "B1234567",
                    "status": "active",
                    "userId": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED, PERSON_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  },
                  "PERSON_REQUIRED": {
                    "summary": "PERSON_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PERSON_REQUIRED",
                        "message": "Only a signed-in person does this, such as an action that asks for a password; an API key never does"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "CONFLICT, IDEMPOTENCY_KEY_REUSED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "CONFLICT": {
                    "summary": "CONFLICT",
                    "value": {
                      "error": {
                        "code": "CONFLICT",
                        "message": "The request conflicts with the present state"
                      }
                    }
                  },
                  "IDEMPOTENCY_KEY_REUSED": {
                    "summary": "IDEMPOTENCY_KEY_REUSED",
                    "value": {
                      "error": {
                        "code": "IDEMPOTENCY_KEY_REUSED",
                        "message": "This idempotency key was used before for a different request"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/drivers/{driverId}": {
      "patch": {
        "operationId": "updateDriver",
        "tags": [
          "Drivers"
        ],
        "summary": "Change a driver",
        "description": "Changes the fields you send and keeps the rest. An inactive driver gets no new routes, and their account no longer sees their routes.",
        "x-permissions": [
          "driver:update",
          "driver:read"
        ],
        "x-feature": "routes",
        "x-rate-limit": {
          "attempts": 60,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "driverId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The driver's id, from your drivers"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 240,
                    "description": "The driver's full name, plain text"
                  },
                  "phone": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 40,
                    "description": "A phone number of 7 to 15 digits, with + and the country code in front or without; spaces, brackets, dots and dashes are dropped. null for none"
                  },
                  "licenseNumber": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 64,
                    "description": "The driving licence number, kept in capitals; null for none"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "inactive"
                    ],
                    "description": "active or inactive. Only a signed-in person changes it for a driver linked to an account, since that decides what the account sees: a key gets PERSON_REQUIRED"
                  },
                  "userId": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid",
                    "description": "The id of the person's account the driver signs in with to see their routes. Only a signed-in person links or unlinks an account: a key leaves it out, or sends it unchanged, and otherwise gets PERSON_REQUIRED"
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "phone": "+905339876543",
                "status": "active"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Driver"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "id": "0199b000-0000-7000-8000-000000000601",
                    "name": "Ayşe Yılmaz",
                    "phone": "+905339876543",
                    "licenseNumber": "B1234567",
                    "status": "active",
                    "userId": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED, PERSON_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  },
                  "PERSON_REQUIRED": {
                    "summary": "PERSON_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PERSON_REQUIRED",
                        "message": "Only a signed-in person does this, such as an action that asks for a password; an API key never does"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ecu-replacements": {
      "get": {
        "operationId": "listEcuReplacements",
        "tags": [
          "Vehicle data"
        ],
        "summary": "Replaced ECUs",
        "description": "Every ECU whose serial number changed at the same address of the same vehicle, newest first, with the serial number before and after as the ECU gave them, in hexadecimal",
        "x-permissions": [
          "vehicle:read",
          "position:read"
        ],
        "x-feature": "vehicle-data",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "description": "How many come at most on one page"
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "The page.next of the page before, to ask for the next page; send it as it came (an opaque cursor)"
          },
          {
            "name": "vehicleId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only this vehicle's replaced ECUs, by its id"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EcuReplacement"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/ListPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000701",
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "plate": "34 API 001",
                      "ecu": {
                        "tr": "Motor kontrol ünitesi",
                        "en": "Engine control unit",
                        "de": "Motorsteuergerät"
                      },
                      "address": 2016,
                      "serialBefore": "3132333435363738",
                      "serialAfter": "3132333435363739",
                      "foundAt": "2026-11-04T08:12:30.000Z"
                    }
                  ],
                  "page": {
                    "next": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/feed": {
      "get": {
        "operationId": "listFeed",
        "tags": [
          "The feed"
        ],
        "summary": "The feed",
        "description": "What happened in your fleet, in the order it was written, from where your last page ended, once it is a minute old and for 14 days. Each entry is of a kind and carries what it is about, the way that thing's own list gives it, with a cursor to go on from right after it. Each kind needs its own permission, and without position:read an entry's place is left out. Without the vehicle data package a drive comes with its time driving and distance only, as the vehicle's drives list gives it. More kinds may come in time, so skip an entry of a kind you do not know and go on after its cursor.",
        "x-permissions": [
          "alert:read",
          "device:read",
          "fault:read",
          "fuel:read",
          "geofence:read",
          "package:read",
          "position:read",
          "vehicle:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 120000,
          "windowSeconds": 60,
          "counts": "items"
        },
        "x-kinds": {
          "vehicle-silent": {
            "permissions": [
              "vehicle:read"
            ],
            "feature": null,
            "group": "fleet"
          },
          "vehicle-heard-again": {
            "permissions": [
              "vehicle:read"
            ],
            "feature": null,
            "group": "fleet"
          },
          "vehicle-faults": {
            "permissions": [
              "fault:read"
            ],
            "feature": "faults",
            "group": "fleet"
          },
          "vehicle-alert": {
            "permissions": [
              "alert:read"
            ],
            "feature": null,
            "group": "fleet"
          },
          "geofence-entered": {
            "permissions": [
              "geofence:read",
              "position:read"
            ],
            "feature": "geofences",
            "group": "fleet"
          },
          "geofence-left": {
            "permissions": [
              "geofence:read",
              "position:read"
            ],
            "feature": "geofences",
            "group": "fleet"
          },
          "fuel-drop": {
            "permissions": [
              "fuel:read"
            ],
            "feature": "fuel-watch",
            "group": "fleet"
          },
          "refuel": {
            "permissions": [
              "fuel:read"
            ],
            "feature": "fuel-watch",
            "group": "fleet"
          },
          "health-alert": {
            "permissions": [
              "device:read",
              "vehicle:read"
            ],
            "feature": null,
            "group": "fleet"
          },
          "health-alert-ended": {
            "permissions": [
              "device:read",
              "vehicle:read"
            ],
            "feature": null,
            "group": "fleet"
          },
          "drive-completed": {
            "permissions": [
              "vehicle:read",
              "position:read"
            ],
            "feature": null,
            "group": "fleet"
          },
          "ecu-replaced": {
            "permissions": [
              "vehicle:read",
              "position:read"
            ],
            "feature": "vehicle-data",
            "group": "fleet"
          },
          "vehicle-added": {
            "permissions": [
              "vehicle:read"
            ],
            "feature": null,
            "group": "management"
          },
          "vehicle-changed": {
            "permissions": [
              "vehicle:read"
            ],
            "feature": null,
            "group": "management"
          },
          "device-assigned": {
            "permissions": [
              "device:read"
            ],
            "feature": null,
            "group": "management"
          },
          "device-installed": {
            "permissions": [
              "device:read",
              "vehicle:read"
            ],
            "feature": null,
            "group": "management"
          },
          "device-removed": {
            "permissions": [
              "device:read",
              "vehicle:read"
            ],
            "feature": null,
            "group": "management"
          },
          "feature-switched": {
            "permissions": [
              "package:read"
            ],
            "feature": null,
            "group": "management"
          },
          "retention-changed": {
            "permissions": [
              "package:read"
            ],
            "feature": null,
            "group": "management"
          },
          "retention-applied": {
            "permissions": [
              "package:read"
            ],
            "feature": null,
            "group": "management"
          }
        },
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512
            },
            "description": "The page.next of the page before, to go on where it ended"
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start here instead, at most 14 days back"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 1000,
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            },
            "description": "How many come at most on one page"
          },
          {
            "name": "kinds",
            "in": "query",
            "required": false,
            "schema": {
              "minItems": 1,
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "vehicle-silent",
                  "vehicle-heard-again",
                  "vehicle-faults",
                  "vehicle-alert",
                  "geofence-entered",
                  "geofence-left",
                  "fuel-drop",
                  "refuel",
                  "health-alert",
                  "health-alert-ended",
                  "drive-completed",
                  "ecu-replaced",
                  "vehicle-added",
                  "vehicle-changed",
                  "device-assigned",
                  "device-installed",
                  "device-removed",
                  "feature-switched",
                  "retention-changed",
                  "retention-applied"
                ]
              }
            },
            "description": "Only entries of these kinds, comma separated. Without it every kind you may read comes; a kind you may not read is refused.",
            "style": "form",
            "explode": false
          },
          {
            "name": "waitSeconds",
            "in": "query",
            "required": false,
            "schema": {
              "default": 0,
              "type": "integer",
              "minimum": 0,
              "maximum": 30
            },
            "description": "When nothing is ready, wait up to this many seconds for the next entry; the platform may wait less. 0 answers at once."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the copy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FeedEntry"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/CopyPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000801",
                      "kind": "vehicle-alert",
                      "cursor": "Wm3kQ9vXEXAMPLE.Pz7cR2EXAMPLE",
                      "customerId": "0199b000-0000-7000-8000-000000000501",
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "writtenAt": "2026-11-04T08:43:05.000Z",
                      "subject": {
                        "id": "0199b000-0000-7000-8000-000000000301",
                        "vehicleId": "0199b000-0000-7000-8000-000000000001",
                        "plate": "34 API 001",
                        "name": "Engine running hot",
                        "signal": "coolant-temperature",
                        "comparison": "above",
                        "threshold": 105,
                        "unit": "°C",
                        "peak": 112.5,
                        "startedAt": "2026-11-04T08:41:12.000Z",
                        "durationSeconds": 95,
                        "place": {
                          "latitude": 41.068,
                          "longitude": 29.011
                        }
                      }
                    },
                    {
                      "id": "0199b000-0000-7000-8000-000000000802",
                      "kind": "geofence-left",
                      "cursor": "Hn4wLp9vEXAMPLE.Qe8tY1EXAMPLE",
                      "customerId": "0199b000-0000-7000-8000-000000000501",
                      "vehicleId": "0199b000-0000-7000-8000-000000000002",
                      "writtenAt": "2026-11-04T08:44:30.000Z",
                      "subject": {
                        "id": "0199b000-0000-7000-8000-000000000302",
                        "geofenceId": "0199b000-0000-7000-8000-000000000701",
                        "geofence": "Depot",
                        "vehicleId": "0199b000-0000-7000-8000-000000000002",
                        "plate": "34 API 002",
                        "start": "entered",
                        "startedAt": "2026-11-04T06:02:10.000Z",
                        "end": "left",
                        "endedAt": "2026-11-04T08:43:58.000Z"
                      }
                    }
                  ],
                  "page": {
                    "next": "q8Xr2vKmT0EXAMPLE.Hn4wLp9EXAMPLE",
                    "more": false
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "410": {
            "description": "CURSOR_EXPIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "CURSOR_EXPIRED": {
                    "summary": "CURSOR_EXPIRED",
                    "value": {
                      "error": {
                        "code": "CURSOR_EXPIRED",
                        "message": "The cursor is too old to go on from; start the copy again, with from to reach further back"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/fleet/open-faults": {
      "get": {
        "operationId": "listFleetOpenFaults",
        "tags": [
          "Trouble codes"
        ],
        "summary": "Open faults per vehicle",
        "description": "How many faults are still open on each vehicle, one request for the whole fleet; a vehicle with none is left out",
        "x-permissions": [
          "fault:read"
        ],
        "x-feature": "faults",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OpenFaults"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "open": 2
                    },
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000002",
                      "open": 1
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/fleet/positions": {
      "get": {
        "operationId": "listNewestPositions",
        "tags": [
          "Vehicles and positions"
        ],
        "summary": "Newest positions",
        "description": "The newest position of each vehicle, null until it reports",
        "x-permissions": [
          "position:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FleetPosition"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "plate": "34 API 001",
                      "label": "Delivery van",
                      "position": {
                        "recordedAt": "2026-11-04T08:41:12.000Z",
                        "latitude": 41.068,
                        "longitude": 29.011,
                        "accuracyM": 4,
                        "speedKmh": 52,
                        "headingDeg": 87,
                        "moving": true,
                        "silentAt": "2026-11-04T08:56:12.000Z",
                        "parkedSince": null
                      }
                    },
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000003",
                      "plate": "34 API 003",
                      "label": "Service car",
                      "position": {
                        "recordedAt": "2026-11-04T08:40:30.000Z",
                        "latitude": 41.042,
                        "longitude": 28.987,
                        "accuracyM": 6,
                        "speedKmh": 0,
                        "headingDeg": null,
                        "moving": false,
                        "silentAt": "2026-11-04T08:55:30.000Z",
                        "parkedSince": "2026-11-04T08:12:40.000Z"
                      }
                    },
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000002",
                      "plate": "34 API 002",
                      "label": null,
                      "position": null
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/fleet/readings": {
      "get": {
        "operationId": "listFleetReadings",
        "tags": [
          "Vehicle data"
        ],
        "summary": "The fleet's newest readings",
        "description": "The newest value of the signals you name for every vehicle that reads them, one request for the whole fleet",
        "x-permissions": [
          "vehicle:read",
          "position:read"
        ],
        "x-feature": "vehicle-data",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "signals",
            "in": "query",
            "required": true,
            "schema": {
              "minItems": 1,
              "maxItems": 32,
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^[a-z\\d-]{1,32}$"
              }
            },
            "description": "The signals to answer, comma separated, 32 at most, each one that means the same on every vehicle",
            "style": "form",
            "explode": false
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FleetReadings"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "recordedAt": "2026-11-04T08:41:12.000Z",
                      "readings": {
                        "fuel-level": 61.5
                      }
                    },
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000002",
                      "recordedAt": "2026-11-04T08:40:58.000Z",
                      "readings": {
                        "fuel-level": 24
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/fuel-events": {
      "get": {
        "operationId": "listFuelEvents",
        "tags": [
          "Fuel"
        ],
        "summary": "Fuel drops and refuels",
        "description": "Each drop and refuel the platform detected in a tank, newest first, with the level before and after and how many litres that is",
        "x-permissions": [
          "fuel:read"
        ],
        "x-feature": "fuel-watch",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "description": "How many come at most on one page"
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "The page.next of the page before, to ask for the next page; send it as it came (an opaque cursor)"
          },
          {
            "name": "vehicleId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only this vehicle's drops and refuels, by its id"
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "drop",
                "refuel"
              ]
            },
            "description": "Only drops, drop, or only refuels, refuel"
          },
          {
            "name": "tank",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "fuel",
                "def"
              ]
            },
            "description": "Only this tank's: fuel, or def for AdBlue"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FuelEvent"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/ListPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000901",
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "plate": "34 API 001",
                      "tank": "fuel",
                      "kind": "drop",
                      "during": "parked",
                      "levelBefore": 62,
                      "levelAfter": 48,
                      "litres": 11.2,
                      "beforeAt": "2026-11-04T01:10:00.000Z",
                      "afterAt": "2026-11-04T01:40:00.000Z",
                      "detectedAt": "2026-11-04T01:52:00.000Z"
                    },
                    {
                      "id": "0199b000-0000-7000-8000-000000000902",
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "plate": "34 API 001",
                      "tank": "fuel",
                      "kind": "refuel",
                      "during": "standing",
                      "levelBefore": 48,
                      "levelAfter": 95,
                      "litres": 37.6,
                      "beforeAt": "2026-11-04T07:05:00.000Z",
                      "afterAt": "2026-11-04T07:14:00.000Z",
                      "detectedAt": "2026-11-04T07:20:00.000Z"
                    }
                  ],
                  "page": {
                    "next": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/fuel-events/{eventId}": {
      "get": {
        "operationId": "getFuelEvent",
        "tags": [
          "Fuel"
        ],
        "summary": "One fuel drop or refuel",
        "description": "A drop or refuel as the list gives it, with where the vehicle stood when the level before was read, which a key without position:read does not see",
        "x-permissions": [
          "fuel:read"
        ],
        "x-feature": "fuel-watch",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The drop's or refuel's id, from the list"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PlacedFuelEvent"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "id": "0199b000-0000-7000-8000-000000000901",
                    "vehicleId": "0199b000-0000-7000-8000-000000000001",
                    "plate": "34 API 001",
                    "tank": "fuel",
                    "kind": "drop",
                    "during": "parked",
                    "levelBefore": 62,
                    "levelAfter": 48,
                    "litres": 11.2,
                    "beforeAt": "2026-11-04T01:10:00.000Z",
                    "afterAt": "2026-11-04T01:40:00.000Z",
                    "detectedAt": "2026-11-04T01:52:00.000Z",
                    "place": {
                      "latitude": 41.068,
                      "longitude": 29.011
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/geofence-limits": {
      "get": {
        "operationId": "getGeofenceLimits",
        "tags": [
          "Geofences"
        ],
        "summary": "Geofence limits",
        "description": "How many geofences you may keep and keep now, the most corners of a polygon and the smallest and largest radius of a circle",
        "x-permissions": [
          "geofence:read"
        ],
        "x-feature": "geofences",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GeofenceLimits"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "geofencesMax": 100,
                    "geofencesUsed": 2,
                    "cornersMax": 50,
                    "radiusMinMeters": 50,
                    "radiusMaxMeters": 30000
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/geofence-stays": {
      "get": {
        "operationId": "listGeofenceStays",
        "tags": [
          "Geofences"
        ],
        "summary": "Stays in geofences",
        "description": "Each time a vehicle was inside a geofence, from when it came in to when it went out, the newest first; a stay still going has no end",
        "x-permissions": [
          "geofence:read",
          "position:read"
        ],
        "x-feature": "geofences",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "description": "How many come at most on one page"
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "The page.next of the page before, to ask for the next page; send it as it came (an opaque cursor)"
          },
          {
            "name": "vehicleId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only this vehicle's stays, by its id"
          },
          {
            "name": "geofenceId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only the stays in this geofence, by its id"
          },
          {
            "name": "inside",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "description": "true for only the stays still going"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GeofenceStay"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/ListPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000302",
                      "geofenceId": "0199b000-0000-7000-8000-000000000701",
                      "geofence": "Depot",
                      "vehicleId": "0199b000-0000-7000-8000-000000000002",
                      "plate": "34 API 002",
                      "start": "entered",
                      "startedAt": "2026-11-04T06:02:10.000Z",
                      "end": "left",
                      "endedAt": "2026-11-04T08:43:58.000Z"
                    },
                    {
                      "id": "0199b000-0000-7000-8000-000000000303",
                      "geofenceId": "0199b000-0000-7000-8000-000000000701",
                      "geofence": "Depot",
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "plate": "34 API 001",
                      "start": "already-inside",
                      "startedAt": "2026-11-02T07:30:00.000Z",
                      "end": null,
                      "endedAt": null
                    }
                  ],
                  "page": {
                    "next": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/geofences": {
      "get": {
        "operationId": "listGeofences",
        "tags": [
          "Geofences"
        ],
        "summary": "Your geofences",
        "description": "Your geofences, each with its shape, what it tells and how many vehicles are inside",
        "x-permissions": [
          "geofence:read"
        ],
        "x-feature": "geofences",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "description": "How many come at most on one page"
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "The page.next of the page before, to ask for the next page; send it as it came (an opaque cursor)"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Geofence"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/ListPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000701",
                      "name": "Depot",
                      "shape": {
                        "kind": "circle",
                        "center": {
                          "latitude": 41.0681,
                          "longitude": 29.0109
                        },
                        "radiusMeters": 250
                      },
                      "tellEntries": true,
                      "tellExits": true,
                      "inside": 2,
                      "createdAt": "2026-11-02T07:30:00.000Z",
                      "shapedAt": "2026-11-02T07:30:00.000Z"
                    },
                    {
                      "id": "0199b000-0000-7000-8000-000000000702",
                      "name": "Harbour gate",
                      "shape": {
                        "kind": "polygon",
                        "corners": [
                          {
                            "latitude": 40.9921,
                            "longitude": 29.0241
                          },
                          {
                            "latitude": 40.9934,
                            "longitude": 29.0263
                          },
                          {
                            "latitude": 40.9917,
                            "longitude": 29.0279
                          }
                        ]
                      },
                      "tellEntries": true,
                      "tellExits": false,
                      "inside": 0,
                      "createdAt": "2026-11-03T09:12:00.000Z",
                      "shapedAt": "2026-11-04T06:45:00.000Z"
                    }
                  ],
                  "page": {
                    "next": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createGeofence",
        "tags": [
          "Geofences"
        ],
        "summary": "Draw a geofence",
        "description": "Draws a geofence; vehicles coming in and going out are judged from then on. Drawing, changing and removing geofences also count together against 60 a minute for your whole organization.",
        "x-permissions": [
          "geofence:configure"
        ],
        "x-feature": "geofences",
        "x-rate-limit": {
          "attempts": 60,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{8,64}$"
            },
            "description": "Your own key for this request, 8 to 64 letters, digits, dashes or underscores. Send the same key when you send the request again after a lost answer, and the first answer comes back instead of a second record"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 160,
                    "description": "A name for the geofence, plain text, unique among your geofences whatever the case"
                  },
                  "shape": {
                    "oneOf": [
                      {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string",
                            "const": "circle"
                          },
                          "center": {
                            "type": "object",
                            "properties": {
                              "latitude": {
                                "type": "number",
                                "minimum": -90,
                                "maximum": 90
                              },
                              "longitude": {
                                "type": "number",
                                "minimum": -180,
                                "maximum": 180
                              }
                            },
                            "required": [
                              "latitude",
                              "longitude"
                            ],
                            "additionalProperties": false
                          },
                          "radiusMeters": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "kind",
                          "center",
                          "radiusMeters"
                        ],
                        "additionalProperties": false
                      },
                      {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string",
                            "const": "polygon"
                          },
                          "corners": {
                            "maxItems": 200,
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "latitude": {
                                  "type": "number",
                                  "minimum": -90,
                                  "maximum": 90
                                },
                                "longitude": {
                                  "type": "number",
                                  "minimum": -180,
                                  "maximum": 180
                                }
                              },
                              "required": [
                                "latitude",
                                "longitude"
                              ],
                              "additionalProperties": false
                            }
                          }
                        },
                        "required": [
                          "kind",
                          "corners"
                        ],
                        "additionalProperties": false
                      }
                    ],
                    "description": "A circle, {kind: circle, center, radiusMeters}, or a polygon, {kind: polygon, corners}, each place {latitude, longitude} in degrees. The geofence limits route says how many corners and how large a radius you may draw"
                  },
                  "tellEntries": {
                    "default": true,
                    "type": "boolean",
                    "description": "Whether a vehicle coming in is told, to your people as a notification and in the feed as geofence-entered; true when left out of a new geofence"
                  },
                  "tellExits": {
                    "default": true,
                    "type": "boolean",
                    "description": "Whether a vehicle going out is told, as a notification and in the feed as geofence-left; true when left out of a new geofence"
                  }
                },
                "required": [
                  "name",
                  "shape"
                ],
                "additionalProperties": false
              },
              "example": {
                "name": "Depot",
                "shape": {
                  "kind": "circle",
                  "center": {
                    "latitude": 41.0681,
                    "longitude": 29.0109
                  },
                  "radiusMeters": 250
                },
                "tellExits": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "What was made",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Geofence"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "id": "0199b000-0000-7000-8000-000000000701",
                    "name": "Depot",
                    "shape": {
                      "kind": "circle",
                      "center": {
                        "latitude": 41.0681,
                        "longitude": 29.0109
                      },
                      "radiusMeters": 250
                    },
                    "tellEntries": true,
                    "tellExits": true,
                    "inside": 0,
                    "createdAt": "2026-11-02T07:30:00.000Z",
                    "shapedAt": "2026-11-02T07:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "CONFLICT, GEOFENCE_LIMIT, GEOFENCE_NAME_TAKEN, IDEMPOTENCY_KEY_REUSED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "CONFLICT": {
                    "summary": "CONFLICT",
                    "value": {
                      "error": {
                        "code": "CONFLICT",
                        "message": "The request conflicts with the present state"
                      }
                    }
                  },
                  "GEOFENCE_LIMIT": {
                    "summary": "GEOFENCE_LIMIT",
                    "value": {
                      "error": {
                        "code": "GEOFENCE_LIMIT",
                        "message": "The customer keeps as many geofences as it may"
                      }
                    }
                  },
                  "GEOFENCE_NAME_TAKEN": {
                    "summary": "GEOFENCE_NAME_TAKEN",
                    "value": {
                      "error": {
                        "code": "GEOFENCE_NAME_TAKEN",
                        "message": "Another geofence has this name"
                      }
                    }
                  },
                  "IDEMPOTENCY_KEY_REUSED": {
                    "summary": "IDEMPOTENCY_KEY_REUSED",
                    "value": {
                      "error": {
                        "code": "IDEMPOTENCY_KEY_REUSED",
                        "message": "This idempotency key was used before for a different request"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/geofences/{geofenceId}": {
      "get": {
        "operationId": "getGeofence",
        "tags": [
          "Geofences"
        ],
        "summary": "A geofence",
        "description": "One geofence, as your geofences list gives it",
        "x-permissions": [
          "geofence:read"
        ],
        "x-feature": "geofences",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "geofenceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The geofence's id, from your geofences"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Geofence"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "id": "0199b000-0000-7000-8000-000000000701",
                    "name": "Depot",
                    "shape": {
                      "kind": "circle",
                      "center": {
                        "latitude": 41.0681,
                        "longitude": 29.0109
                      },
                      "radiusMeters": 250
                    },
                    "tellEntries": true,
                    "tellExits": true,
                    "inside": 2,
                    "createdAt": "2026-11-02T07:30:00.000Z",
                    "shapedAt": "2026-11-02T07:30:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "updateGeofence",
        "tags": [
          "Geofences"
        ],
        "summary": "Change a geofence",
        "description": "Changes the fields you send and keeps the rest. A new shape is judged from the moment it is drawn. Drawing, changing and removing geofences also count together against 60 a minute for your whole organization.",
        "x-permissions": [
          "geofence:configure",
          "geofence:read"
        ],
        "x-feature": "geofences",
        "x-rate-limit": {
          "attempts": 60,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "geofenceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The geofence's id, from your geofences"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 160,
                    "description": "A name for the geofence, plain text, unique among your geofences whatever the case"
                  },
                  "shape": {
                    "oneOf": [
                      {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string",
                            "const": "circle"
                          },
                          "center": {
                            "type": "object",
                            "properties": {
                              "latitude": {
                                "type": "number",
                                "minimum": -90,
                                "maximum": 90
                              },
                              "longitude": {
                                "type": "number",
                                "minimum": -180,
                                "maximum": 180
                              }
                            },
                            "required": [
                              "latitude",
                              "longitude"
                            ],
                            "additionalProperties": false
                          },
                          "radiusMeters": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "kind",
                          "center",
                          "radiusMeters"
                        ],
                        "additionalProperties": false
                      },
                      {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string",
                            "const": "polygon"
                          },
                          "corners": {
                            "maxItems": 200,
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "latitude": {
                                  "type": "number",
                                  "minimum": -90,
                                  "maximum": 90
                                },
                                "longitude": {
                                  "type": "number",
                                  "minimum": -180,
                                  "maximum": 180
                                }
                              },
                              "required": [
                                "latitude",
                                "longitude"
                              ],
                              "additionalProperties": false
                            }
                          }
                        },
                        "required": [
                          "kind",
                          "corners"
                        ],
                        "additionalProperties": false
                      }
                    ],
                    "description": "A circle, {kind: circle, center, radiusMeters}, or a polygon, {kind: polygon, corners}, each place {latitude, longitude} in degrees. The geofence limits route says how many corners and how large a radius you may draw"
                  },
                  "tellEntries": {
                    "type": "boolean",
                    "description": "Whether a vehicle coming in is told, to your people as a notification and in the feed as geofence-entered; true when left out of a new geofence"
                  },
                  "tellExits": {
                    "type": "boolean",
                    "description": "Whether a vehicle going out is told, as a notification and in the feed as geofence-left; true when left out of a new geofence"
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "tellExits": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Geofence"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "id": "0199b000-0000-7000-8000-000000000701",
                    "name": "Depot",
                    "shape": {
                      "kind": "circle",
                      "center": {
                        "latitude": 41.0681,
                        "longitude": 29.0109
                      },
                      "radiusMeters": 250
                    },
                    "tellEntries": true,
                    "tellExits": false,
                    "inside": 2,
                    "createdAt": "2026-11-02T07:30:00.000Z",
                    "shapedAt": "2026-11-02T07:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "GEOFENCE_NAME_TAKEN",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "GEOFENCE_NAME_TAKEN": {
                    "summary": "GEOFENCE_NAME_TAKEN",
                    "value": {
                      "error": {
                        "code": "GEOFENCE_NAME_TAKEN",
                        "message": "Another geofence has this name"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteGeofence",
        "tags": [
          "Geofences"
        ],
        "summary": "Remove a geofence",
        "description": "Removes a geofence: vehicles inside it are no longer judged, their stays end without being told, and the stays it had keep their history. Drawing, changing and removing geofences also count together against 60 a minute for your whole organization.",
        "x-permissions": [
          "geofence:configure"
        ],
        "x-feature": "geofences",
        "x-rate-limit": {
          "attempts": 60,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "geofenceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The geofence's id, from your geofences"
          }
        ],
        "responses": {
          "204": {
            "description": "Done; the answer has no body"
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/positions": {
      "get": {
        "operationId": "copyPositions",
        "tags": [
          "Vehicles and positions"
        ],
        "summary": "Copy positions",
        "description": "Every vehicle's positions in the order they were received, from where your last page ended, once they are a minute old",
        "x-permissions": [
          "position:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 120000,
          "windowSeconds": 60,
          "counts": "items"
        },
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512
            },
            "description": "The page.next of the page before, to go on where it ended"
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start a new copy here instead. Within the last 14 days this counts against nothing; further back, within your kept data, it counts against 5 starts a day for each key or person"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 1000,
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            },
            "description": "How many come at most on one page"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the copy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CopiedPosition"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/CopyPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "deviceId": "0199b000-0000-7000-8000-000000000401",
                      "serial": "EXAMPLE-0001",
                      "recordedAt": "2026-11-04T08:41:12.000Z",
                      "receivedAt": "2026-11-04T08:41:44.000Z",
                      "latitude": 41.068,
                      "longitude": 29.011,
                      "accuracyM": 4,
                      "speedKmh": 52,
                      "headingDeg": 87,
                      "moving": true
                    },
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000002",
                      "deviceId": "0199b000-0000-7000-8000-000000000402",
                      "serial": "EXAMPLE-0002",
                      "recordedAt": "2026-11-04T08:12:30.000Z",
                      "receivedAt": "2026-11-04T08:41:45.000Z",
                      "latitude": 41.0712,
                      "longitude": 29.0146,
                      "accuracyM": 6,
                      "speedKmh": 0,
                      "headingDeg": null,
                      "moving": false
                    }
                  ],
                  "page": {
                    "next": "q8Xr2vKmT0EXAMPLE.Hn4wLp9EXAMPLE",
                    "more": false
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "410": {
            "description": "CURSOR_EXPIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "CURSOR_EXPIRED": {
                    "summary": "CURSOR_EXPIRED",
                    "value": {
                      "error": {
                        "code": "CURSOR_EXPIRED",
                        "message": "The cursor is too old to go on from; start the copy again, with from to reach further back"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/readings": {
      "get": {
        "operationId": "copyReadings",
        "tags": [
          "Vehicle data"
        ],
        "summary": "Copy readings",
        "description": "Every vehicle's readings in the order they were received, from where your last page ended, once they are a minute old. Each row a page goes through counts toward the limit, also one left out for holding none of your signals.",
        "x-permissions": [
          "vehicle:read",
          "position:read"
        ],
        "x-feature": "vehicle-data",
        "x-rate-limit": {
          "attempts": 120000,
          "windowSeconds": 60,
          "counts": "items"
        },
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512
            },
            "description": "The page.next of the page before, to go on where it ended"
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start a new copy here instead. Within the last 14 days this counts against nothing; further back, within your kept data, it counts against 5 starts a day for each key or person"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 1000,
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            },
            "description": "How many come at most on one page"
          },
          {
            "name": "signals",
            "in": "query",
            "required": false,
            "schema": {
              "minItems": 1,
              "maxItems": 32,
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^[a-z\\d-]{1,32}$"
              }
            },
            "description": "Only these signals, comma separated, 32 at most, by the names a vehicle's signals list gives",
            "style": "form",
            "explode": false
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the copy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CopiedReadings"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/CopyPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "deviceId": "0199b000-0000-7000-8000-000000000401",
                      "serial": "EXAMPLE-0001",
                      "recordedAt": "2026-11-04T08:41:12.000Z",
                      "receivedAt": "2026-11-04T08:41:44.000Z",
                      "readings": {
                        "speed": 52,
                        "engine-speed": 1840,
                        "fuel-level": 61.5
                      }
                    },
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000002",
                      "deviceId": "0199b000-0000-7000-8000-000000000402",
                      "serial": "EXAMPLE-0002",
                      "recordedAt": "2026-11-04T08:40:58.000Z",
                      "receivedAt": "2026-11-04T08:41:46.000Z",
                      "readings": {
                        "coolant-temperature": 88,
                        "odometer": 84219.8
                      }
                    }
                  ],
                  "page": {
                    "next": "q8Xr2vKmT0EXAMPLE.Hn4wLp9EXAMPLE",
                    "more": false
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "410": {
            "description": "CURSOR_EXPIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "CURSOR_EXPIRED": {
                    "summary": "CURSOR_EXPIRED",
                    "value": {
                      "error": {
                        "code": "CURSOR_EXPIRED",
                        "message": "The cursor is too old to go on from; start the copy again, with from to reach further back"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/reports/driving": {
      "get": {
        "operationId": "getDrivingReport",
        "tags": [
          "Drives and reports"
        ],
        "summary": "Driving style report",
        "description": "How each vehicle was driven from one moment to another, a year at most: idling, high load, harsh moves and top speed",
        "x-permissions": [
          "vehicle:read",
          "position:read"
        ],
        "x-feature": "vehicle-data",
        "x-rate-limit": {
          "attempts": 30,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The first moment of the period"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The last moment of the period"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DrivingReport"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "vehicles": [
                      {
                        "vehicleId": "0199b000-0000-7000-8000-000000000001",
                        "plate": "34 API 001",
                        "label": "Delivery van",
                        "drives": 42,
                        "completed": 41,
                        "drivingSeconds": 61200,
                        "distanceKm": 1284.6,
                        "idleSeconds": 5400,
                        "idleShare": 0.088,
                        "highLoadSeconds": 3100,
                        "highLoadShare": 0.051,
                        "harshAccelerations": {
                          "count": 3,
                          "per100Km": 0.23
                        },
                        "harshBrakings": {
                          "count": 5,
                          "per100Km": 0.39
                        },
                        "topSpeedKmh": 118,
                        "score": {
                          "value": 89,
                          "band": "very-good",
                          "parts": [
                            {
                              "figure": "harshBrakings",
                              "value": 0.39,
                              "points": 1
                            },
                            {
                              "figure": "harshAccelerations",
                              "value": 0.23,
                              "points": 1
                            },
                            {
                              "figure": "idleShare",
                              "value": 0.088,
                              "points": 6
                            },
                            {
                              "figure": "highLoadShare",
                              "value": 0.051,
                              "points": 3
                            }
                          ]
                        }
                      }
                    ],
                    "total": {
                      "drives": 42,
                      "completed": 41,
                      "drivingSeconds": 61200,
                      "distanceKm": 1284.6,
                      "idleSeconds": 5400,
                      "idleShare": 0.088,
                      "highLoadSeconds": 3100,
                      "highLoadShare": 0.051,
                      "harshAccelerations": {
                        "count": 3,
                        "per100Km": null
                      },
                      "harshBrakings": {
                        "count": 5,
                        "per100Km": null
                      },
                      "topSpeedKmh": 118,
                      "score": {
                        "value": 89,
                        "band": "very-good",
                        "parts": []
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/reports/fuel-distance": {
      "get": {
        "operationId": "getFuelDistanceReport",
        "tags": [
          "Drives and reports"
        ],
        "summary": "Fuel and distance report",
        "description": "Each vehicle's drives, distance and fuel from one moment to another, a year at most, and their total",
        "x-permissions": [
          "vehicle:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 30,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The first moment of the period"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The last moment of the period"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/FuelDistanceReport"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "includes": {
                      "fuel": true,
                      "fuelEvents": true
                    },
                    "vehicles": [
                      {
                        "vehicleId": "0199b000-0000-7000-8000-000000000001",
                        "plate": "34 API 001",
                        "label": "Delivery van",
                        "drives": 42,
                        "completed": 41,
                        "drivingSeconds": 61200,
                        "distanceKm": 1284.6,
                        "trackKm": 12.3,
                        "fuelLitres": 108.2,
                        "litresPer100Km": 8.42,
                        "fuelEvents": {
                          "refuels": 2,
                          "refuelLitres": 96.5,
                          "drops": 0,
                          "dropLitres": null
                        }
                      }
                    ],
                    "total": {
                      "drives": 42,
                      "completed": 41,
                      "drivingSeconds": 61200,
                      "distanceKm": 1284.6,
                      "trackKm": 12.3,
                      "fuelLitres": 108.2,
                      "litresPer100Km": null,
                      "fuelEvents": {
                        "refuels": 2,
                        "refuelLitres": 96.5,
                        "drops": 0,
                        "dropLitres": null
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/reports/power-take-off": {
      "get": {
        "operationId": "getPowerTakeOffReport",
        "tags": [
          "Drives and reports"
        ],
        "summary": "Power take-off report",
        "description": "How long each vehicle's power take-off ran from one moment to another, a year at most",
        "x-permissions": [
          "vehicle:read"
        ],
        "x-feature": "power-take-off",
        "x-rate-limit": {
          "attempts": 30,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The first moment of the period"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The last moment of the period"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PowerTakeOffReport"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "vehicles": [
                      {
                        "vehicleId": "0199b000-0000-7000-8000-000000000001",
                        "plate": "34 API 001",
                        "label": "Delivery van",
                        "drives": 42,
                        "completed": 41,
                        "drivingSeconds": 61200,
                        "distanceKm": 1284.6,
                        "powerTakeOffSeconds": 9000,
                        "powerTakeOffShare": 0.147
                      }
                    ],
                    "total": {
                      "drives": 42,
                      "completed": 41,
                      "drivingSeconds": 61200,
                      "distanceKm": 1284.6,
                      "powerTakeOffSeconds": 9000,
                      "powerTakeOffShare": 0.147
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vehicle-faults": {
      "get": {
        "operationId": "listVehicleFaults",
        "tags": [
          "Trouble codes"
        ],
        "summary": "Trouble codes",
        "description": "Your trouble codes, newest first, each with its meaning and the part of the vehicle that reported it",
        "x-permissions": [
          "fault:read"
        ],
        "x-feature": "faults",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "description": "How many come at most on one page"
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "The page.next of the page before, to ask for the next page; send it as it came (an opaque cursor)"
          },
          {
            "name": "vehicleId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only this vehicle's trouble codes, by its id"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Fault"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/ListPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000601",
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "plate": "34 API 001",
                      "code": "P0301",
                      "system": "powertrain",
                      "meaning": {
                        "tr": "Silindir 1 ateşleme hatası algılandı",
                        "en": "Cylinder 1 misfire detected",
                        "de": "Zylinder 1 Fehlzündung erkannt"
                      },
                      "ecu": {
                        "tr": "Motor kontrol ünitesi",
                        "en": "Engine control unit",
                        "de": "Motorsteuergerät"
                      },
                      "clearable": false,
                      "status": "stored",
                      "appearedAt": "2026-11-04T08:12:30.000Z",
                      "clearedAt": null,
                      "clearingRefusal": null,
                      "clearingWarning": null
                    }
                  ],
                  "page": {
                    "next": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vehicles": {
      "get": {
        "operationId": "listVehicles",
        "tags": [
          "Vehicles and positions"
        ],
        "summary": "Your vehicles",
        "description": "Your vehicles, each with the tracker installed in it, whether that tracker has the vehicle kit, and the hardware your package still needs",
        "x-permissions": [
          "vehicle:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "description": "How many come at most on one page"
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "The page.next of the page before, to ask for the next page; send it as it came (an opaque cursor)"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Vehicle"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/ListPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000001",
                      "plate": "34 API 001",
                      "label": "Delivery van",
                      "vin": "VF1EXAMPLE0000001",
                      "status": "active",
                      "deviceId": "0199b000-0000-7000-8000-000000000401",
                      "kit": true,
                      "missingCapabilities": []
                    },
                    {
                      "id": "0199b000-0000-7000-8000-000000000002",
                      "plate": "34 API 002",
                      "label": null,
                      "vin": null,
                      "status": "active",
                      "deviceId": null,
                      "kit": false,
                      "missingCapabilities": []
                    }
                  ],
                  "page": {
                    "next": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vehicles/{vehicleId}": {
      "patch": {
        "operationId": "updateVehicle",
        "tags": [
          "Vehicles and positions"
        ],
        "summary": "Label a vehicle",
        "description": "Gives a vehicle a label of your own, or takes it away; the plate, VIN and tracker stay as the platform keeps them. Answers the vehicle as your vehicle list gives it, and the feed tells the change as vehicle-changed.",
        "x-permissions": [
          "vehicle:label",
          "vehicle:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 60,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The vehicle's id, from your vehicle list"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 200,
                    "description": "A name your organization gives the vehicle, plain text; null to take it away"
                  }
                },
                "required": [
                  "label"
                ],
                "additionalProperties": false
              },
              "example": {
                "label": "Cold chain van"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Vehicle"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "id": "0199b000-0000-7000-8000-000000000001",
                    "plate": "34 API 001",
                    "label": "Cold chain van",
                    "vin": "VF1EXAMPLE0000001",
                    "status": "active",
                    "deviceId": "0199b000-0000-7000-8000-000000000401",
                    "kit": true,
                    "missingCapabilities": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vehicles/{vehicleId}/drives": {
      "get": {
        "operationId": "listVehicleDrives",
        "tags": [
          "Drives and reports"
        ],
        "summary": "A vehicle's drives",
        "description": "A vehicle's drives that started from one moment to another, 32 days at most, with their figures once they are worked out",
        "x-permissions": [
          "vehicle:read",
          "position:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The vehicle's id, from your vehicle list"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The first moment of the period"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The last moment of the period"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Drive"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000101",
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "startedAt": "2026-11-04T07:58:40.000Z",
                      "endedAt": "2026-11-04T08:41:12.000Z",
                      "startedBy": "crank",
                      "figures": {
                        "drivingSeconds": 2350,
                        "distance": {
                          "km": 31.4,
                          "by": "odometer"
                        },
                        "topSpeedKmh": 96,
                        "fuel": {
                          "litres": 2.6,
                          "by": "counter"
                        },
                        "idleSeconds": 190,
                        "highLoadSeconds": 240,
                        "topCoolantC": 91,
                        "topOilC": 104,
                        "harshAccelerations": 1,
                        "harshBrakings": 0
                      },
                      "score": {
                        "value": 81,
                        "band": "good",
                        "parts": [
                          {
                            "figure": "harshBrakings",
                            "value": 0,
                            "points": 0
                          },
                          {
                            "figure": "harshAccelerations",
                            "value": 3.18,
                            "points": 8
                          },
                          {
                            "figure": "idleShare",
                            "value": 0.074,
                            "points": 5
                          },
                          {
                            "figure": "highLoadShare",
                            "value": 0.094,
                            "points": 6
                          }
                        ]
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vehicles/{vehicleId}/fuel-tanks": {
      "get": {
        "operationId": "listVehicleFuelTanks",
        "tags": [
          "Fuel"
        ],
        "summary": "A vehicle's fuel tanks",
        "description": "The vehicle's fuel and AdBlue tanks, each with its size where the platform knows it and whether the vehicle gives its level",
        "x-permissions": [
          "fuel:read"
        ],
        "x-feature": "fuel-watch",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The vehicle's id, from your vehicle list"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FuelTank"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "tank": "fuel",
                      "litres": 80,
                      "given": true
                    },
                    {
                      "tank": "def",
                      "litres": null,
                      "given": false
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vehicles/{vehicleId}/harsh-moves": {
      "get": {
        "operationId": "listVehicleHarshMoves",
        "tags": [
          "Drives and reports"
        ],
        "summary": "A vehicle's harsh moves",
        "description": "A vehicle's harsh accelerations and brakings from one moment to another, 32 days at most, oldest first: when each was strongest, how strong in m/s², the speed it began and ended at, and where it happened when that is known. Only moves at least as strong as the thresholds that count a drive's harsh moves come.",
        "x-permissions": [
          "vehicle:read",
          "position:read"
        ],
        "x-feature": "vehicle-data",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The vehicle's id, from your vehicle list"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The first moment of the period"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The last moment of the period"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/HarshMove"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "kind": "acceleration",
                      "peakAt": "2026-11-04T08:03:12.000Z",
                      "peakMs2": 3.4,
                      "fromKmh": 0,
                      "toKmh": 41,
                      "place": {
                        "latitude": 41.0151,
                        "longitude": 28.9793
                      }
                    },
                    {
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "kind": "braking",
                      "peakAt": "2026-11-04T08:12:31.000Z",
                      "peakMs2": 4.6,
                      "fromKmh": 87,
                      "toKmh": 52,
                      "place": null
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vehicles/{vehicleId}/installations": {
      "get": {
        "operationId": "listVehicleInstallations",
        "tags": [
          "Vehicles and positions"
        ],
        "summary": "A vehicle's trackers over time",
        "description": "Every time a tracker was installed in the vehicle, oldest first, with when it was taken out again; the one still in has no removedAt",
        "x-permissions": [
          "vehicle:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The vehicle's id, from your vehicle list"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "description": "How many come at most on one page"
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "description": "The page.next of the page before, to ask for the next page; send it as it came (an opaque cursor)"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Installation"
                      }
                    },
                    "page": {
                      "$ref": "#/components/schemas/ListPage"
                    }
                  },
                  "required": [
                    "data",
                    "page"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "0199b000-0000-7000-8000-000000000b01",
                      "deviceId": "0199b000-0000-7000-8000-000000000402",
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "installedAt": "2026-09-01T08:00:00.000Z",
                      "removedAt": "2026-10-12T09:10:00.000Z"
                    },
                    {
                      "id": "0199b000-0000-7000-8000-000000000b02",
                      "deviceId": "0199b000-0000-7000-8000-000000000401",
                      "vehicleId": "0199b000-0000-7000-8000-000000000001",
                      "installedAt": "2026-10-12T09:30:00.000Z",
                      "removedAt": null
                    }
                  ],
                  "page": {
                    "next": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vehicles/{vehicleId}/readings": {
      "get": {
        "operationId": "getVehicleReadings",
        "tags": [
          "Vehicle data"
        ],
        "summary": "Newest readings",
        "description": "A vehicle's newest value of each signal its tracker reads",
        "x-permissions": [
          "vehicle:read",
          "position:read"
        ],
        "x-feature": "vehicle-data",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The vehicle's id, from your vehicle list"
          },
          {
            "name": "kinds",
            "in": "query",
            "required": false,
            "schema": {
              "default": [],
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "measure",
                  "counter",
                  "state",
                  "text"
                ]
              }
            },
            "description": "The kinds of signal to answer, comma separated. Measures, counters and states always come; add text for values in words.",
            "style": "form",
            "explode": false
          },
          {
            "name": "signals",
            "in": "query",
            "required": false,
            "schema": {
              "minItems": 1,
              "maxItems": 32,
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^[a-z\\d-]{1,32}$"
              }
            },
            "description": "Only these signals, comma separated, 32 at most, each one the vehicle's signals list names. They come whatever their kind.",
            "style": "form",
            "explode": false
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/VehicleReadings"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "recordedAt": "2026-11-04T08:41:12.000Z",
                    "readings": {
                      "speed": 52,
                      "engine-speed": 1726,
                      "coolant-temperature": 88,
                      "fuel-level": 63.5
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vehicles/{vehicleId}/readings/series": {
      "get": {
        "operationId": "getVehicleReadingSeries",
        "tags": [
          "Vehicle data"
        ],
        "summary": "Readings over time",
        "description": "A vehicle's readings from one moment to another in at most 240 buckets, each signal by its kind",
        "x-permissions": [
          "vehicle:read",
          "position:read"
        ],
        "x-feature": "vehicle-data",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The vehicle's id, from your vehicle list"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The first moment of the period"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The last moment of the period"
          },
          {
            "name": "kinds",
            "in": "query",
            "required": false,
            "schema": {
              "default": [],
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "measure",
                  "counter",
                  "state",
                  "text"
                ]
              }
            },
            "description": "The kinds of signal to answer, comma separated. Measures, counters and states always come; add text for values in words.",
            "style": "form",
            "explode": false
          },
          {
            "name": "signals",
            "in": "query",
            "required": false,
            "schema": {
              "minItems": 1,
              "maxItems": 32,
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^[a-z\\d-]{1,32}$"
              }
            },
            "description": "Only these signals, comma separated, 32 at most, each one the vehicle's signals list names. They come whatever their kind.",
            "style": "form",
            "explode": false
          },
          {
            "name": "bucketSeconds",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 60,
              "maximum": 2678400
            },
            "description": "How long a bucket is, in seconds: at least as long as the period divided into 240, at most the whole period. Without it the shortest is taken."
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ReadingSeries"
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "from": "2026-11-04T08:00:00.000Z",
                    "to": "2026-11-04T09:00:00.000Z",
                    "bucketSeconds": 900,
                    "quiet": [],
                    "series": [
                      {
                        "signal": "speed",
                        "kind": "measure",
                        "unit": "km/h",
                        "paceSeconds": 20,
                        "rare": false,
                        "points": [
                          {
                            "at": "2026-11-04T08:00:00.000Z",
                            "low": 0,
                            "high": 74,
                            "mean": 41.2,
                            "last": 52
                          }
                        ]
                      },
                      {
                        "signal": "odometer",
                        "kind": "counter",
                        "unit": "km",
                        "paceSeconds": 60,
                        "rare": false,
                        "points": [
                          {
                            "at": "2026-11-04T08:00:00.000Z",
                            "low": 84211.4,
                            "high": 84219.8,
                            "last": 84219.8
                          }
                        ]
                      },
                      {
                        "signal": "driver-working-state",
                        "kind": "state",
                        "paceSeconds": null,
                        "rare": true,
                        "points": [
                          {
                            "at": "2026-11-04T08:00:00.000Z",
                            "state": "drive"
                          }
                        ]
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vehicles/{vehicleId}/signals": {
      "get": {
        "operationId": "listVehicleSignals",
        "tags": [
          "Vehicle data"
        ],
        "summary": "A vehicle's signals",
        "description": "Each signal a vehicle's tracker reads: its name, its labels in each language, its kind, and the unit and decimals of a number or the states of a state",
        "x-permissions": [
          "vehicle:read",
          "position:read"
        ],
        "x-feature": "vehicle-data",
        "x-rate-limit": {
          "attempts": 120,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The vehicle's id, from your vehicle list"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "signals": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Signal"
                          }
                        }
                      },
                      "required": [
                        "signals"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": {
                    "signals": [
                      {
                        "name": "speed",
                        "labels": {
                          "tr": "Hız",
                          "en": "Speed",
                          "de": "Geschwindigkeit"
                        },
                        "base": true,
                        "kind": "measure",
                        "unit": "km/h",
                        "decimals": 0
                      },
                      {
                        "name": "fuel-level",
                        "labels": {
                          "tr": "Yakıt seviyesi",
                          "en": "Fuel level",
                          "de": "Tankfüllstand"
                        },
                        "base": true,
                        "kind": "measure",
                        "unit": "%",
                        "decimals": 1
                      },
                      {
                        "name": "odometer",
                        "labels": {
                          "tr": "Kilometre",
                          "en": "Odometer",
                          "de": "Kilometerstand"
                        },
                        "base": true,
                        "kind": "counter",
                        "unit": "km",
                        "decimals": 1
                      },
                      {
                        "name": "driver-working-state",
                        "labels": {
                          "tr": "Sürücü çalışma durumu",
                          "en": "Driver working state",
                          "de": "Arbeitszustand des Fahrers"
                        },
                        "base": false,
                        "kind": "state",
                        "states": [
                          {
                            "name": "rest",
                            "labels": {
                              "tr": "Dinlenme",
                              "en": "Rest",
                              "de": "Ruhezeit"
                            }
                          },
                          {
                            "name": "available",
                            "labels": {
                              "tr": "Hazır bekleme",
                              "en": "Available",
                              "de": "Bereitschaft"
                            }
                          },
                          {
                            "name": "work",
                            "labels": {
                              "tr": "Diğer iş",
                              "en": "Other work",
                              "de": "Andere Arbeit"
                            }
                          },
                          {
                            "name": "drive",
                            "labels": {
                              "tr": "Sürüş",
                              "en": "Driving",
                              "de": "Lenkzeit"
                            }
                          }
                        ]
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vehicles/{vehicleId}/track": {
      "get": {
        "operationId": "getVehicleTrack",
        "tags": [
          "Vehicles and positions"
        ],
        "summary": "A vehicle's track",
        "description": "A vehicle's positions from one moment to another, seven days at most",
        "x-permissions": [
          "position:read"
        ],
        "x-feature": null,
        "x-rate-limit": {
          "attempts": 60,
          "windowSeconds": 60,
          "counts": "requests"
        },
        "parameters": [
          {
            "name": "vehicleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The vehicle's id, from your vehicle list"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The first moment of the period"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "The last moment of the period"
          }
        ],
        "responses": {
          "200": {
            "description": "The answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TrackPoint"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "recordedAt": "2026-11-04T08:41:12.000Z",
                      "latitude": 41.068,
                      "longitude": 29.011,
                      "accuracyM": 4,
                      "speedKmh": 52,
                      "headingDeg": 87,
                      "moving": true
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "summary": "INVALID_REQUEST",
                    "value": {
                      "error": {
                        "code": "INVALID_REQUEST",
                        "message": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "UNAUTHENTICATED": {
                    "summary": "UNAUTHENTICATED",
                    "value": {
                      "error": {
                        "code": "UNAUTHENTICATED",
                        "message": "No valid bearer token, session or live API key came with the request"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "FEATURE_DISABLED, FORBIDDEN, PASSWORD_CHANGE_REQUIRED",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "FEATURE_DISABLED": {
                    "summary": "FEATURE_DISABLED",
                    "value": {
                      "error": {
                        "code": "FEATURE_DISABLED",
                        "message": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
                      }
                    }
                  },
                  "FORBIDDEN": {
                    "summary": "FORBIDDEN",
                    "value": {
                      "error": {
                        "code": "FORBIDDEN",
                        "message": "The caller lacks a permission this needs, or the request came from another site"
                      }
                    }
                  },
                  "PASSWORD_CHANGE_REQUIRED": {
                    "summary": "PASSWORD_CHANGE_REQUIRED",
                    "value": {
                      "error": {
                        "code": "PASSWORD_CHANGE_REQUIRED",
                        "message": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "NOT_FOUND": {
                    "summary": "NOT_FOUND",
                    "value": {
                      "error": {
                        "code": "NOT_FOUND",
                        "message": "Nothing in the caller's reach has this id"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "summary": "RATE_LIMITED",
                    "value": {
                      "error": {
                        "code": "RATE_LIMITED",
                        "message": "Too many requests; the Retry-After header says when to try again"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Failure"
                },
                "examples": {
                  "INTERNAL_ERROR": {
                    "summary": "INTERNAL_ERROR",
                    "value": {
                      "error": {
                        "code": "INTERNAL_ERROR",
                        "message": "Something went wrong on our side"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "feedEntry": {
      "post": {
        "operationId": "receiveFeedEntry",
        "tags": [
          "The feed"
        ],
        "summary": "A feed entry pushed to your endpoint",
        "description": "Each delivery is one POST with one feed entry as its JSON body, exactly as GET /api/v1/feed lists it, with its cursor. Entries go in the feed's order, one at a time per webhook, at least a minute after they happened, and an entry is sent again until your endpoint takes it (at-least-once delivery), so keep each by its id. webhook-id is the entry's id. Skip a kind you do not know. Every delivery and challenge is signed with the webhook's own secret, following the Standard Webhooks headers, so their libraries check it too. The signed content is webhook-id, a dot, webhook-timestamp, a dot and the raw body. Its HMAC-SHA256, keyed with the base64-decoded secret after whsec_, is base64 in webhook-signature after v1, and the header may hold several, separated by spaces. Compare in constant time, on the body as it arrived, before parsing it.",
        "security": [],
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The message's id; for a delivery, the feed entry's id, the same on every attempt"
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "When it was sent, in whole seconds since 1970 UTC; refuse one more than 300 seconds from your clock"
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "v1, and the base64 HMAC-SHA256 of the id, a dot, the timestamp, a dot and the raw body, keyed with the base64-decoded secret after whsec_; several are separated by spaces"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "One feed entry, as the feed lists it",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedEntry"
              },
              "example": {
                "id": "0199b000-0000-7000-8000-000000000801",
                "kind": "vehicle-alert",
                "cursor": "Wm3kQ9vXEXAMPLE.Pz7cR2EXAMPLE",
                "customerId": "0199b000-0000-7000-8000-000000000501",
                "vehicleId": "0199b000-0000-7000-8000-000000000001",
                "writtenAt": "2026-11-04T08:43:05.000Z",
                "subject": {
                  "id": "0199b000-0000-7000-8000-000000000301",
                  "vehicleId": "0199b000-0000-7000-8000-000000000001",
                  "plate": "34 API 001",
                  "name": "Engine running hot",
                  "signal": "coolant-temperature",
                  "comparison": "above",
                  "threshold": 105,
                  "unit": "°C",
                  "peak": 112.5,
                  "startedAt": "2026-11-04T08:41:12.000Z",
                  "durationSeconds": 95,
                  "place": {
                    "latitude": 41.068,
                    "longitude": 29.011
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Taken; any 2xx answer is"
          },
          "3XX": {
            "description": "A failure: a redirect is never followed, and the same message is sent again later"
          },
          "4XX": {
            "description": "A failure: the same message is sent again later"
          },
          "5XX": {
            "description": "A failure: the same message is sent again later"
          }
        }
      }
    },
    "challenge": {
      "post": {
        "operationId": "answerWebhookChallenge",
        "tags": [
          "The feed"
        ],
        "summary": "The check that proves your endpoint holds the secret",
        "description": "Before anything is sent, check the endpoint in the panel. We send it a signed challenge, and it answers 2xx with JSON whose answer is the challenge's HMAC-SHA256 in base64, keyed with the secret as a signature is, which proves it holds the secret. A webhook that has not passed its check is sent nothing; a paused one goes on where it stopped once it passes again.",
        "security": [],
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The message's id; for a delivery, the feed entry's id, the same on every attempt"
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "When it was sent, in whole seconds since 1970 UTC; refuse one more than 300 seconds from your clock"
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "v1, and the base64 HMAC-SHA256 of the id, a dot, the timestamp, a dot and the raw body, keyed with the base64-decoded secret after whsec_; several are separated by spaces"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The challenge",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookChallenge"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The challenge signed with the webhook's secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookChallengeAnswer"
                }
              }
            }
          },
          "3XX": {
            "description": "A failure: a redirect is never followed, and the same message is sent again later"
          },
          "4XX": {
            "description": "A failure: the same message is sent again later"
          },
          "5XX": {
            "description": "A failure: the same message is sent again later"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key, or the token of a signed-in person"
      }
    },
    "schemas": {
      "AlertEpisode": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle the tracker was in, null when it was in none"
          },
          "plate": {
            "description": "That vehicle's plate, null when the tracker was in none",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string",
            "description": "The alert's name"
          },
          "signal": {
            "description": "The signal it watches, as its rule names it; null for a signal only one kind of vehicle has",
            "type": [
              "string",
              "null"
            ]
          },
          "comparison": {
            "type": "string",
            "enum": [
              "above",
              "below"
            ],
            "description": "Whether it fires above or below its threshold"
          },
          "threshold": {
            "description": "The threshold in the unit, null where the platform sets it",
            "type": [
              "number",
              "null"
            ]
          },
          "unit": {
            "description": "The unit of threshold and peak",
            "type": [
              "string",
              "null"
            ]
          },
          "peak": {
            "type": "number",
            "description": "The furthest the value went past the threshold"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the value first went past the threshold"
          },
          "durationSeconds": {
            "type": "integer",
            "description": "How long it stayed past the threshold, in seconds"
          },
          "place": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "latitude": {
                "type": "number"
              },
              "longitude": {
                "type": "number"
              }
            },
            "required": [
              "latitude",
              "longitude"
            ],
            "description": "Where it began, null when unknown or without position:read"
          }
        },
        "required": [
          "id",
          "vehicleId",
          "plate",
          "name",
          "signal",
          "comparison",
          "threshold",
          "unit",
          "peak",
          "startedAt",
          "durationSeconds",
          "place"
        ]
      },
      "AlertRule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "signal": {
            "type": "string",
            "description": "The signal it watches, one of the signals this list names, or battery-low"
          },
          "comparison": {
            "type": "string",
            "enum": [
              "above",
              "below"
            ]
          },
          "threshold": {
            "description": "The value the signal must pass; null for battery-low, whose threshold the platform sets",
            "type": [
              "number",
              "null"
            ]
          },
          "unit": {
            "description": "The unit of the threshold, such as °C or V; empty when it has none, null for a signal no longer offered",
            "type": [
              "string",
              "null"
            ]
          },
          "holdSeconds": {
            "type": "integer",
            "description": "How long the signal stays past the threshold before an alert"
          },
          "on": {
            "type": "boolean",
            "description": "Whether the rule raises alerts now"
          }
        },
        "required": [
          "id",
          "name",
          "signal",
          "comparison",
          "threshold",
          "unit",
          "holdSeconds",
          "on"
        ]
      },
      "AlertRules": {
        "type": "object",
        "properties": {
          "rules": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AlertRule"
            },
            "description": "Every rule, on or off"
          },
          "signals": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AlertSignal"
            },
            "description": "Every signal a rule may watch, with the thresholds it takes"
          }
        },
        "required": [
          "rules",
          "signals"
        ]
      },
      "AlertSignal": {
        "type": "object",
        "properties": {
          "signal": {
            "type": "string"
          },
          "unit": {
            "description": "The unit of the threshold, such as °C or V; empty when it has none, null for a signal no longer offered",
            "type": [
              "string",
              "null"
            ]
          },
          "threshold": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ThresholdRange"
              },
              {
                "$ref": "#/components/schemas/PlatformThreshold"
              }
            ],
            "description": "customer: a rule names a threshold from min to max in steps of step; platform: a rule names this comparison and no threshold",
            "discriminator": {
              "propertyName": "by",
              "mapping": {
                "customer": "#/components/schemas/ThresholdRange",
                "platform": "#/components/schemas/PlatformThreshold"
              }
            }
          },
          "usable": {
            "type": "boolean",
            "description": "Whether your package lets a rule watch it now"
          }
        },
        "required": [
          "signal",
          "unit",
          "threshold",
          "usable"
        ]
      },
      "CopiedPosition": {
        "type": "object",
        "properties": {
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "deviceId": {
            "type": "string",
            "format": "uuid",
            "description": "The tracker that sent the position"
          },
          "serial": {
            "type": "string",
            "description": "That tracker's serial number"
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the platform received it, the order a copy follows"
          },
          "recordedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the tracker measured the position"
          },
          "latitude": {
            "type": "number",
            "description": "Degrees north, WGS 84"
          },
          "longitude": {
            "type": "number",
            "description": "Degrees east, WGS 84"
          },
          "accuracyM": {
            "description": "How far off the position may be, in metres",
            "type": [
              "number",
              "null"
            ]
          },
          "speedKmh": {
            "description": "Speed in km/h, null when unknown or without the vehicle data package",
            "type": [
              "number",
              "null"
            ]
          },
          "headingDeg": {
            "description": "Direction of travel in degrees clockwise from north",
            "type": [
              "number",
              "null"
            ]
          },
          "moving": {
            "type": "boolean",
            "description": "Whether the vehicle was moving at 5 km/h or more"
          }
        },
        "required": [
          "vehicleId",
          "deviceId",
          "serial",
          "receivedAt",
          "recordedAt",
          "latitude",
          "longitude",
          "accuracyM",
          "speedKmh",
          "headingDeg",
          "moving"
        ]
      },
      "CopiedReadings": {
        "type": "object",
        "properties": {
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "deviceId": {
            "type": "string",
            "format": "uuid",
            "description": "The tracker that sent the readings"
          },
          "serial": {
            "type": "string",
            "description": "That tracker's serial number"
          },
          "recordedAt": {
            "type": "string",
            "format": "date-time",
            "description": "The second the tracker measured these values in"
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the platform received them, the order a copy follows; a row that gains a value comes again with a later one"
          },
          "readings": {
            "type": "object",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "type": [
                "number",
                "string"
              ]
            },
            "description": "Each signal's value by its name: a number in the signal's unit, a state's name, or a text. A state the platform does not name comes as its number"
          }
        },
        "required": [
          "vehicleId",
          "deviceId",
          "serial",
          "recordedAt",
          "receivedAt",
          "readings"
        ]
      },
      "CopyPage": {
        "type": "object",
        "properties": {
          "next": {
            "type": "string",
            "description": "An opaque cursor: send it as after to go on where this page ended; a copy never ends, so it is never null"
          },
          "more": {
            "type": "boolean",
            "description": "True when more is waiting, so ask again at once; false when you are up to date, so ask again a minute or longer later"
          }
        },
        "required": [
          "next",
          "more"
        ]
      },
      "CounterSeries": {
        "type": "object",
        "properties": {
          "signal": {
            "type": "string",
            "description": "The signal's name"
          },
          "kind": {
            "type": "string",
            "const": "counter"
          },
          "unit": {
            "type": "string",
            "description": "The unit of the values"
          },
          "paceSeconds": {
            "description": "The usual time between two readings of the signal, null when it was never read twice in a row",
            "type": [
              "number",
              "null"
            ]
          },
          "rare": {
            "type": "boolean",
            "description": "True for a signal read now and then rather than steadily, such as once a drive, best drawn as points"
          },
          "points": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "The start of the bucket"
                },
                "low": {
                  "type": "number",
                  "description": "The lowest value in the bucket"
                },
                "high": {
                  "type": "number",
                  "description": "The highest value in the bucket"
                },
                "last": {
                  "type": "number",
                  "description": "The last value in the bucket"
                }
              },
              "required": [
                "at",
                "low",
                "high",
                "last"
              ]
            }
          }
        },
        "required": [
          "signal",
          "kind",
          "unit",
          "paceSeconds",
          "rare",
          "points"
        ]
      },
      "CounterSignal": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The signal's name, as readings and series name it and as signals takes it"
          },
          "labels": {
            "description": "What the signal is, in each language",
            "$ref": "#/components/schemas/Labels"
          },
          "base": {
            "type": "boolean",
            "description": "True for a core reading every tracker gives wherever the vehicle offers it, false for one the vehicle's own setup adds"
          },
          "kind": {
            "type": "string",
            "const": "counter",
            "description": "A total that only grows, such as the odometer"
          },
          "unit": {
            "type": "string",
            "description": "The unit of its values, such as km/h, °C or L"
          },
          "decimals": {
            "type": "integer",
            "minimum": 0,
            "description": "The most digits after the decimal point a value has"
          }
        },
        "required": [
          "name",
          "labels",
          "base",
          "kind",
          "unit",
          "decimals"
        ]
      },
      "Device": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "serial": {
            "type": "string",
            "description": "The tracker's serial number"
          },
          "label": {
            "description": "A name given to the tracker, if any",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "retired"
            ]
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is installed in, null while it is in none"
          }
        },
        "required": [
          "id",
          "serial",
          "label",
          "status",
          "vehicleId"
        ]
      },
      "DeviceAssignedEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "const": "device-assigned"
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/Device"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "DeviceHealth": {
        "type": "object",
        "properties": {
          "signalDbm": {
            "description": "The mobile signal the tracker last had, in dBm",
            "type": [
              "integer",
              "null"
            ]
          },
          "supplyMillivolts": {
            "description": "The vehicle's supply the tracker last measured, in millivolts; 0 when it had no supply from the vehicle",
            "type": [
              "integer",
              "null"
            ]
          },
          "gnss": {
            "type": [
              "string",
              "null"
            ],
            "description": "silent: no position available; searching: looking for a position; fixed: a position found",
            "enum": [
              "silent",
              "searching",
              "fixed",
              null
            ]
          },
          "lastFixAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the tracker last had a position fix"
          },
          "uptimeSeconds": {
            "description": "How long the tracker had been running, in seconds",
            "type": [
              "integer",
              "null"
            ]
          },
          "healthReportedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the tracker last told how it is; every field above is from then, all null before"
          },
          "lastReportAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the tracker was last heard"
          }
        },
        "required": [
          "signalDbm",
          "supplyMillivolts",
          "gnss",
          "lastFixAt",
          "uptimeSeconds",
          "healthReportedAt",
          "lastReportAt"
        ]
      },
      "DeviceHealthAlert": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "condition": {
            "type": "string",
            "enum": [
              "silent"
            ],
            "description": "What is wrong: silent, the tracker has not been heard for longer than it should"
          },
          "since": {
            "type": "string",
            "format": "date-time",
            "description": "Since when the condition holds"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the platform raised the alert"
          }
        },
        "required": [
          "id",
          "condition",
          "since",
          "startedAt"
        ]
      },
      "Drive": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "endedAt": {
            "type": "string",
            "format": "date-time"
          },
          "startedBy": {
            "type": "string",
            "enum": [
              "motion",
              "crank",
              "charge-with-traffic",
              "engine-running",
              "ignition"
            ],
            "description": "What told the tracker the vehicle was in use: motion, an engine start, the battery charging while the vehicle's electronics were awake, the engine running, or the ignition"
          },
          "figures": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/DriveFigures"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the drive came to, null until it is worked out shortly after the drive ends; each figure is null when the vehicle does not give the values it needs"
          },
          "score": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/DrivingScore"
              },
              {
                "type": "null"
              }
            ],
            "description": "A driving score from 0 to 100, null below 10 km or without harsh move figures"
          }
        },
        "required": [
          "id",
          "vehicleId",
          "startedAt",
          "endedAt",
          "startedBy",
          "figures",
          "score"
        ]
      },
      "DriveCompletedEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "const": "drive-completed"
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/Drive"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "DriveFigures": {
        "type": "object",
        "properties": {
          "drivingSeconds": {
            "description": "The time the vehicle moved, in seconds; the rest of the drive it stood",
            "type": [
              "number",
              "null"
            ]
          },
          "distance": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "km": {
                "type": "number"
              },
              "by": {
                "type": "string",
                "enum": [
                  "odometer",
                  "track"
                ],
                "description": "Where the distance came from: the vehicle's odometer, or the track of its positions"
              }
            },
            "required": [
              "km",
              "by"
            ]
          },
          "topSpeedKmh": {
            "description": "The highest speed, in km/h",
            "type": [
              "number",
              "null"
            ]
          },
          "fuel": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "litres": {
                "type": "number",
                "description": "The fuel the drive used, in litres"
              },
              "by": {
                "type": "string",
                "enum": [
                  "counter",
                  "litres",
                  "level"
                ],
                "description": "Where the fuel came from: the vehicle's fuel used counter, the litres in its tank, or its fuel level times the tank size"
              }
            },
            "required": [
              "litres",
              "by"
            ]
          },
          "idleSeconds": {
            "description": "The time the engine ran while the vehicle stood, in seconds",
            "type": [
              "number",
              "null"
            ]
          },
          "highLoadSeconds": {
            "description": "The time the engine ran at high load, in seconds",
            "type": [
              "number",
              "null"
            ]
          },
          "topCoolantC": {
            "description": "The highest coolant temperature, in °C",
            "type": [
              "number",
              "null"
            ]
          },
          "topOilC": {
            "description": "The highest engine oil temperature, in °C",
            "type": [
              "number",
              "null"
            ]
          },
          "harshAccelerations": {
            "description": "How many times the vehicle sped up harshly",
            "type": [
              "integer",
              "null"
            ]
          },
          "harshBrakings": {
            "description": "How many times the vehicle braked harshly",
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "drivingSeconds",
          "distance",
          "topSpeedKmh",
          "fuel",
          "idleSeconds",
          "highLoadSeconds",
          "topCoolantC",
          "topOilC",
          "harshAccelerations",
          "harshBrakings"
        ]
      },
      "Driver": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "phone": {
            "description": "The driver's phone number as given, without spaces or separators, if any",
            "type": [
              "string",
              "null"
            ]
          },
          "licenseNumber": {
            "description": "The driving licence number in capitals, if given",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive"
            ],
            "description": "active, or inactive once the driver no longer drives for you: a route goes only to an active driver, and an inactive driver's account no longer sees its routes"
          },
          "userId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The person's account the driver signs in with to see their routes, null without one; only a person links or unlinks it"
          }
        },
        "required": [
          "id",
          "name",
          "phone",
          "licenseNumber",
          "status",
          "userId"
        ]
      },
      "DrivingReport": {
        "type": "object",
        "properties": {
          "vehicles": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "vehicleId": {
                  "type": "string",
                  "format": "uuid"
                },
                "plate": {
                  "type": "string"
                },
                "label": {
                  "description": "A name your organization gave the vehicle, if any",
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "drives": {
                  "type": "integer",
                  "description": "How many drives started in the period"
                },
                "completed": {
                  "type": "integer",
                  "description": "How many of them are complete, their figures worked out; the other figures sum only those, and the rest complete shortly after they end"
                },
                "drivingSeconds": {
                  "type": "number",
                  "description": "The time the vehicles moved, in seconds"
                },
                "distanceKm": {
                  "type": "number",
                  "description": "The distance driven, in km"
                },
                "idleSeconds": {
                  "description": "The time the engine ran while the vehicle stood, in seconds",
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "idleShare": {
                  "description": "The share of the drives' time the engine ran while the vehicle stood, from 0 to 1",
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "highLoadSeconds": {
                  "description": "The time the engine ran at high load, in seconds",
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "highLoadShare": {
                  "description": "The share of the drives' time the engine ran at high load, from 0 to 1",
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "harshAccelerations": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "per100Km": {
                      "description": "The count per 100 km, null below 10 km driven",
                      "type": [
                        "number",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "count",
                    "per100Km"
                  ]
                },
                "harshBrakings": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "per100Km": {
                      "description": "The count per 100 km, null below 10 km driven",
                      "type": [
                        "number",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "count",
                    "per100Km"
                  ]
                },
                "topSpeedKmh": {
                  "description": "The highest speed, in km/h",
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "score": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/DrivingScore"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "A driving score from 0 to 100, null below 10 km or without harsh move figures"
                }
              },
              "required": [
                "vehicleId",
                "plate",
                "label",
                "drives",
                "completed",
                "drivingSeconds",
                "distanceKm",
                "idleSeconds",
                "idleShare",
                "highLoadSeconds",
                "highLoadShare",
                "harshAccelerations",
                "harshBrakings",
                "topSpeedKmh",
                "score"
              ]
            }
          },
          "total": {
            "type": "object",
            "properties": {
              "drives": {
                "type": "integer",
                "description": "How many drives started in the period"
              },
              "completed": {
                "type": "integer",
                "description": "How many of them are complete, their figures worked out; the other figures sum only those, and the rest complete shortly after they end"
              },
              "drivingSeconds": {
                "type": "number",
                "description": "The time the vehicles moved, in seconds"
              },
              "distanceKm": {
                "type": "number",
                "description": "The distance driven, in km"
              },
              "idleSeconds": {
                "description": "The time the engine ran while the vehicle stood, in seconds",
                "type": [
                  "number",
                  "null"
                ]
              },
              "idleShare": {
                "description": "The share of the drives' time the engine ran while the vehicle stood, from 0 to 1",
                "type": [
                  "number",
                  "null"
                ]
              },
              "highLoadSeconds": {
                "description": "The time the engine ran at high load, in seconds",
                "type": [
                  "number",
                  "null"
                ]
              },
              "highLoadShare": {
                "description": "The share of the drives' time the engine ran at high load, from 0 to 1",
                "type": [
                  "number",
                  "null"
                ]
              },
              "harshAccelerations": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "count": {
                    "type": "integer"
                  },
                  "per100Km": {
                    "description": "The count per 100 km, null below 10 km driven",
                    "type": [
                      "number",
                      "null"
                    ]
                  }
                },
                "required": [
                  "count",
                  "per100Km"
                ]
              },
              "harshBrakings": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "count": {
                    "type": "integer"
                  },
                  "per100Km": {
                    "description": "The count per 100 km, null below 10 km driven",
                    "type": [
                      "number",
                      "null"
                    ]
                  }
                },
                "required": [
                  "count",
                  "per100Km"
                ]
              },
              "topSpeedKmh": {
                "description": "The highest speed, in km/h",
                "type": [
                  "number",
                  "null"
                ]
              },
              "score": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/DrivingScore"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "A driving score from 0 to 100, null below 10 km or without harsh move figures"
              }
            },
            "required": [
              "drives",
              "completed",
              "drivingSeconds",
              "distanceKm",
              "idleSeconds",
              "idleShare",
              "highLoadSeconds",
              "highLoadShare",
              "harshAccelerations",
              "harshBrakings",
              "topSpeedKmh",
              "score"
            ]
          }
        },
        "required": [
          "vehicles",
          "total"
        ]
      },
      "DrivingScore": {
        "type": "object",
        "properties": {
          "value": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "The score, 100 being the best"
          },
          "band": {
            "type": "string",
            "enum": [
              "very-good",
              "good",
              "needs-attention",
              "poor"
            ],
            "description": "very-good from 85, good from 70, needs-attention from 50, poor below"
          },
          "parts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "figure": {
                  "type": "string",
                  "enum": [
                    "harshBrakings",
                    "harshAccelerations",
                    "idleShare",
                    "highLoadShare"
                  ],
                  "description": "The figure that took points off"
                },
                "value": {
                  "type": "number",
                  "minimum": 0,
                  "description": "The figure's value: harsh moves per 100 km, or a share of the time from 0 to 1"
                },
                "points": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "The points it took off"
                }
              },
              "required": [
                "figure",
                "value",
                "points"
              ]
            },
            "description": "What took points off the score; the points add up to what it lost"
          }
        },
        "required": [
          "value",
          "band",
          "parts"
        ]
      },
      "EcuReplacedEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "const": "ecu-replaced"
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/EcuReplacement"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "EcuReplacement": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "plate": {
            "type": "string"
          },
          "ecu": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Labels"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the replaced control unit does, in each language, null when unknown"
          },
          "address": {
            "type": "integer",
            "minimum": 0,
            "description": "Where the control unit answers in the vehicle, which tells two units apart"
          },
          "serialBefore": {
            "type": "string",
            "description": "Its serial number before, in hexadecimal as it gave it"
          },
          "serialAfter": {
            "type": "string",
            "description": "Its serial number after, in hexadecimal as it gave it"
          },
          "foundAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the tracker found the change"
          }
        },
        "required": [
          "id",
          "vehicleId",
          "plate",
          "ecu",
          "address",
          "serialBefore",
          "serialAfter",
          "foundAt"
        ]
      },
      "Failure": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "ALERT_RULE_LIMIT",
                  "CONFLICT",
                  "CURSOR_EXPIRED",
                  "FEATURE_DISABLED",
                  "FORBIDDEN",
                  "GEOFENCE_LIMIT",
                  "GEOFENCE_NAME_TAKEN",
                  "IDEMPOTENCY_KEY_REUSED",
                  "INTERNAL_ERROR",
                  "INVALID_REQUEST",
                  "KEY_NOT_ALLOWED",
                  "METHOD_NOT_ALLOWED",
                  "NOT_FOUND",
                  "PASSWORD_CHANGE_REQUIRED",
                  "PERSON_REQUIRED",
                  "RATE_LIMITED",
                  "UNAUTHENTICATED"
                ],
                "description": "What went wrong; it stays the same, so build on it"
              },
              "message": {
                "type": "string",
                "description": "The same for people to read; its words may change"
              },
              "details": {
                "description": "Text values that say more, each under a key its code's meaning names. No code a key route answers carries details today, so an INVALID_REQUEST names the refused value in its message instead",
                "type": "object",
                "propertyNames": {
                  "type": "string"
                },
                "additionalProperties": {
                  "type": "string"
                }
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Fault": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "plate": {
            "type": [
              "string",
              "null"
            ]
          },
          "code": {
            "type": "string",
            "description": "The trouble code as the vehicle gave it"
          },
          "system": {
            "type": [
              "string",
              "null"
            ],
            "description": "The part of the vehicle the code's letter names, null for a code without one",
            "enum": [
              "powertrain",
              "chassis",
              "body",
              "network",
              null
            ]
          },
          "meaning": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Labels"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the code means, in each language, null when unknown"
          },
          "ecu": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Labels"
              },
              {
                "type": "null"
              }
            ],
            "description": "What the control unit that reported it does, in each language, null when unknown"
          },
          "clearable": {
            "type": "boolean",
            "description": "Whether the person asking may clear this code now. Clearing is done in the panel; an API key never clears a fault, so a key always sees false"
          },
          "status": {
            "type": "string",
            "enum": [
              "previous",
              "pending",
              "stored",
              "permanent"
            ],
            "description": "How the vehicle holds the code: previously active, pending until confirmed, stored, or permanent until the vehicle itself sees it fixed"
          },
          "appearedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the code first came"
          },
          "clearedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the vehicle stopped reporting it, null while it is open"
          },
          "clearingRefusal": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the person asking cannot clear it now in the panel: no tracker installed, the tracker needs an update, the tracker has not reached that control unit yet, or a support visit. Null when there is nothing to say, so always null to an API key, which never clears a fault",
            "enum": [
              "no-tracker",
              "old-firmware",
              "not-clearable",
              "support-visit",
              null
            ]
          },
          "clearingWarning": {
            "type": [
              "string",
              "null"
            ],
            "description": "What to know before clearing it in the panel: clearing an emissions code restarts the vehicle's own emission checks; a tachograph code comes back while its cause remains. Null unless it is clearable, so always null to an API key",
            "enum": [
              "emissions",
              "tachograph",
              null
            ]
          }
        },
        "required": [
          "id",
          "vehicleId",
          "plate",
          "code",
          "system",
          "meaning",
          "ecu",
          "clearable",
          "status",
          "appearedAt",
          "clearedAt",
          "clearingRefusal",
          "clearingWarning"
        ]
      },
      "FeatureSwitch": {
        "type": "object",
        "properties": {
          "feature": {
            "type": "string",
            "enum": [
              "routes",
              "faults",
              "vehicle-data",
              "recurring-visits",
              "route-optimization",
              "geofences",
              "fault-clearing",
              "fuel-watch",
              "power-take-off",
              "api"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "from": {
            "type": "string",
            "format": "date-time",
            "description": "When the switch took effect"
          }
        },
        "required": [
          "feature",
          "enabled",
          "from"
        ]
      },
      "FeatureSwitchedEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "const": "feature-switched"
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/FeatureSwitch"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "FeedEntry": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/VehicleSilentEntry"
          },
          {
            "$ref": "#/components/schemas/VehicleHeardAgainEntry"
          },
          {
            "$ref": "#/components/schemas/VehicleFaultsEntry"
          },
          {
            "$ref": "#/components/schemas/VehicleAlertEntry"
          },
          {
            "$ref": "#/components/schemas/GeofenceStayEntry"
          },
          {
            "$ref": "#/components/schemas/FuelEventEntry"
          },
          {
            "$ref": "#/components/schemas/HealthAlertEntry"
          },
          {
            "$ref": "#/components/schemas/DriveCompletedEntry"
          },
          {
            "$ref": "#/components/schemas/EcuReplacedEntry"
          },
          {
            "$ref": "#/components/schemas/VehicleEntry"
          },
          {
            "$ref": "#/components/schemas/DeviceAssignedEntry"
          },
          {
            "$ref": "#/components/schemas/InstallationEntry"
          },
          {
            "$ref": "#/components/schemas/FeatureSwitchedEntry"
          },
          {
            "$ref": "#/components/schemas/RetentionChangedEntry"
          },
          {
            "$ref": "#/components/schemas/RetentionAppliedEntry"
          }
        ],
        "discriminator": {
          "propertyName": "kind",
          "mapping": {
            "vehicle-silent": "#/components/schemas/VehicleSilentEntry",
            "vehicle-heard-again": "#/components/schemas/VehicleHeardAgainEntry",
            "vehicle-faults": "#/components/schemas/VehicleFaultsEntry",
            "vehicle-alert": "#/components/schemas/VehicleAlertEntry",
            "geofence-entered": "#/components/schemas/GeofenceStayEntry",
            "geofence-left": "#/components/schemas/GeofenceStayEntry",
            "fuel-drop": "#/components/schemas/FuelEventEntry",
            "refuel": "#/components/schemas/FuelEventEntry",
            "health-alert": "#/components/schemas/HealthAlertEntry",
            "health-alert-ended": "#/components/schemas/HealthAlertEntry",
            "drive-completed": "#/components/schemas/DriveCompletedEntry",
            "ecu-replaced": "#/components/schemas/EcuReplacedEntry",
            "vehicle-added": "#/components/schemas/VehicleEntry",
            "vehicle-changed": "#/components/schemas/VehicleEntry",
            "device-assigned": "#/components/schemas/DeviceAssignedEntry",
            "device-installed": "#/components/schemas/InstallationEntry",
            "device-removed": "#/components/schemas/InstallationEntry",
            "feature-switched": "#/components/schemas/FeatureSwitchedEntry",
            "retention-changed": "#/components/schemas/RetentionChangedEntry",
            "retention-applied": "#/components/schemas/RetentionAppliedEntry"
          }
        }
      },
      "FleetPosition": {
        "type": "object",
        "properties": {
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "plate": {
            "type": "string"
          },
          "label": {
            "description": "A name your organization gave the vehicle, if any",
            "type": [
              "string",
              "null"
            ]
          },
          "position": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/NewestPosition"
              },
              {
                "type": "null"
              }
            ],
            "description": "The newest position, null until the vehicle reports"
          }
        },
        "required": [
          "vehicleId",
          "plate",
          "label",
          "position"
        ]
      },
      "FleetReadings": {
        "type": "object",
        "properties": {
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "recordedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the newest of these values was measured"
          },
          "readings": {
            "type": "object",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "type": [
                "number",
                "string"
              ]
            },
            "description": "Each signal's value by its name: a number in the signal's unit, a state's name, or a text. A state the platform does not name comes as its number"
          }
        },
        "required": [
          "vehicleId",
          "recordedAt",
          "readings"
        ]
      },
      "FuelDistanceReport": {
        "type": "object",
        "properties": {
          "includes": {
            "type": "object",
            "properties": {
              "fuel": {
                "type": "boolean",
                "description": "Whether the fuel figures are given; they need vehicle data"
              },
              "fuelEvents": {
                "type": "boolean",
                "description": "Whether the refuels and drops are given; they need the fuel watch package and fuel:read"
              }
            },
            "required": [
              "fuel",
              "fuelEvents"
            ],
            "description": "Which columns this report fills, by what your package and key allow"
          },
          "vehicles": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "vehicleId": {
                  "type": "string",
                  "format": "uuid"
                },
                "plate": {
                  "type": "string"
                },
                "label": {
                  "description": "A name your organization gave the vehicle, if any",
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "drives": {
                  "type": "integer",
                  "description": "How many drives started in the period"
                },
                "completed": {
                  "type": "integer",
                  "description": "How many of them are complete, their figures worked out; the other figures sum only those, and the rest complete shortly after they end"
                },
                "drivingSeconds": {
                  "type": "number",
                  "description": "The time the vehicles moved, in seconds"
                },
                "distanceKm": {
                  "type": "number",
                  "description": "The distance driven, in km"
                },
                "trackKm": {
                  "type": "number",
                  "description": "The part of the distance taken from the track of positions, where no odometer gave it"
                },
                "fuelLitres": {
                  "description": "The fuel used, in litres",
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "litresPer100Km": {
                  "description": "The fuel used per 100 km, over the drives whose fuel is known",
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "fuelEvents": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "properties": {
                    "refuels": {
                      "type": "integer"
                    },
                    "refuelLitres": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "drops": {
                      "type": "integer",
                      "description": "How many sudden fuel drops were found"
                    },
                    "dropLitres": {
                      "type": [
                        "number",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "refuels",
                    "refuelLitres",
                    "drops",
                    "dropLitres"
                  ]
                }
              },
              "required": [
                "vehicleId",
                "plate",
                "label",
                "drives",
                "completed",
                "drivingSeconds",
                "distanceKm",
                "trackKm",
                "fuelLitres",
                "litresPer100Km",
                "fuelEvents"
              ]
            }
          },
          "total": {
            "type": "object",
            "properties": {
              "drives": {
                "type": "integer",
                "description": "How many drives started in the period"
              },
              "completed": {
                "type": "integer",
                "description": "How many of them are complete, their figures worked out; the other figures sum only those, and the rest complete shortly after they end"
              },
              "drivingSeconds": {
                "type": "number",
                "description": "The time the vehicles moved, in seconds"
              },
              "distanceKm": {
                "type": "number",
                "description": "The distance driven, in km"
              },
              "trackKm": {
                "type": "number",
                "description": "The part of the distance taken from the track of positions, where no odometer gave it"
              },
              "fuelLitres": {
                "description": "The fuel used, in litres",
                "type": [
                  "number",
                  "null"
                ]
              },
              "litresPer100Km": {
                "description": "The fuel used per 100 km, over the drives whose fuel is known",
                "type": [
                  "number",
                  "null"
                ]
              },
              "fuelEvents": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "refuels": {
                    "type": "integer"
                  },
                  "refuelLitres": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "drops": {
                    "type": "integer",
                    "description": "How many sudden fuel drops were found"
                  },
                  "dropLitres": {
                    "type": [
                      "number",
                      "null"
                    ]
                  }
                },
                "required": [
                  "refuels",
                  "refuelLitres",
                  "drops",
                  "dropLitres"
                ]
              }
            },
            "required": [
              "drives",
              "completed",
              "drivingSeconds",
              "distanceKm",
              "trackKm",
              "fuelLitres",
              "litresPer100Km",
              "fuelEvents"
            ]
          }
        },
        "required": [
          "includes",
          "vehicles",
          "total"
        ]
      },
      "FuelEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "plate": {
            "type": "string"
          },
          "tank": {
            "type": "string",
            "enum": [
              "fuel",
              "def"
            ],
            "description": "The fuel tank, or def for AdBlue"
          },
          "kind": {
            "type": "string",
            "enum": [
              "drop",
              "refuel"
            ]
          },
          "during": {
            "type": "string",
            "enum": [
              "parked",
              "standing"
            ],
            "description": "Whether the vehicle was parked, or stood with the ignition on"
          },
          "levelBefore": {
            "type": "number",
            "description": "The tank's level before, in percent"
          },
          "levelAfter": {
            "type": "number",
            "description": "The tank's level after, in percent"
          },
          "litres": {
            "description": "How many litres that is, null without the tank's size",
            "type": [
              "number",
              "null"
            ]
          },
          "beforeAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the level before was read"
          },
          "afterAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the level after was read"
          },
          "detectedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the platform detected the drop or refuel"
          }
        },
        "required": [
          "id",
          "vehicleId",
          "plate",
          "tank",
          "kind",
          "during",
          "levelBefore",
          "levelAfter",
          "litres",
          "beforeAt",
          "afterAt",
          "detectedAt"
        ]
      },
      "FuelEventEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "fuel-drop",
              "refuel"
            ]
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/FuelEvent"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "FuelTank": {
        "type": "object",
        "properties": {
          "tank": {
            "type": "string",
            "enum": [
              "fuel",
              "def"
            ],
            "description": "The fuel tank, or def for AdBlue"
          },
          "litres": {
            "description": "The tank's size in litres, null until the platform sets it",
            "type": [
              "integer",
              "null"
            ]
          },
          "given": {
            "type": "boolean",
            "description": "Whether the vehicle gives this tank's level, which drops and refuels are detected from"
          }
        },
        "required": [
          "tank",
          "litres",
          "given"
        ]
      },
      "Geofence": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Unique among your geofences, whatever the case"
          },
          "shape": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/GeofenceCircle"
              },
              {
                "$ref": "#/components/schemas/GeofencePolygon"
              }
            ],
            "description": "A circle around a centre, or a polygon by its corners",
            "discriminator": {
              "propertyName": "kind",
              "mapping": {
                "circle": "#/components/schemas/GeofenceCircle",
                "polygon": "#/components/schemas/GeofencePolygon"
              }
            }
          },
          "tellEntries": {
            "type": "boolean",
            "description": "Whether a vehicle coming in is told: a notification to your people who see geofences and positions, and a geofence-entered entry in the feed"
          },
          "tellExits": {
            "type": "boolean",
            "description": "Whether a vehicle going out is told, as a notification and a geofence-left entry in the feed"
          },
          "inside": {
            "description": "How many vehicles are inside it now; null unless the caller holds both geofence:read and position:read",
            "type": [
              "integer",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "shapedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When its shape was last drawn; positions from before it are not judged against the new shape"
          }
        },
        "required": [
          "id",
          "name",
          "shape",
          "tellEntries",
          "tellExits",
          "inside",
          "createdAt",
          "shapedAt"
        ]
      },
      "GeofenceCircle": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "const": "circle"
          },
          "center": {
            "$ref": "#/components/schemas/Place"
          },
          "radiusMeters": {
            "type": "integer",
            "description": "The circle's radius in metres"
          }
        },
        "required": [
          "kind",
          "center",
          "radiusMeters"
        ]
      },
      "GeofenceLimits": {
        "type": "object",
        "properties": {
          "geofencesMax": {
            "type": "integer",
            "description": "How many geofences you may keep"
          },
          "geofencesUsed": {
            "type": "integer",
            "description": "How many you keep now"
          },
          "cornersMax": {
            "type": "integer",
            "description": "The most corners a polygon may have"
          },
          "radiusMinMeters": {
            "type": "integer",
            "description": "The smallest radius of a circle"
          },
          "radiusMaxMeters": {
            "type": "integer",
            "description": "The largest radius of a circle"
          }
        },
        "required": [
          "geofencesMax",
          "geofencesUsed",
          "cornersMax",
          "radiusMinMeters",
          "radiusMaxMeters"
        ]
      },
      "GeofencePolygon": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "const": "polygon"
          },
          "corners": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Place"
            },
            "description": "The corners in the order they are joined, the last one back to the first"
          }
        },
        "required": [
          "kind",
          "corners"
        ]
      },
      "GeofenceStay": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "geofenceId": {
            "type": "string",
            "format": "uuid"
          },
          "geofence": {
            "type": "string",
            "description": "The geofence's name"
          },
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "plate": {
            "type": "string"
          },
          "start": {
            "type": "string",
            "enum": [
              "entered",
              "already-inside",
              "first-seen"
            ],
            "description": "How the stay began: the vehicle entered; it was inside when the geofence was drawn; or it was first seen inside"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": [
              "string",
              "null"
            ],
            "description": "How the stay ended: the vehicle left, the geofence was reshaped or removed, the vehicle left the fleet or lost its tracker, or geofences were switched off; null while it lasts",
            "enum": [
              "left",
              "geofence-changed",
              "geofence-removed",
              "vehicle-gone",
              "geofences-off",
              null
            ]
          },
          "endedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "geofenceId",
          "geofence",
          "vehicleId",
          "plate",
          "start",
          "startedAt",
          "end",
          "endedAt"
        ]
      },
      "GeofenceStayEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "geofence-entered",
              "geofence-left"
            ]
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/GeofenceStay"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "HarshMove": {
        "type": "object",
        "properties": {
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "acceleration",
              "braking"
            ]
          },
          "peakAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the move was strongest"
          },
          "peakMs2": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "How strong it was at its peak, in m/s²"
          },
          "fromKmh": {
            "type": "integer",
            "minimum": 0,
            "description": "The speed it began at, in km/h"
          },
          "toKmh": {
            "type": "integer",
            "minimum": 0,
            "description": "The speed it ended at, in km/h"
          },
          "place": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "latitude": {
                "type": "number"
              },
              "longitude": {
                "type": "number"
              }
            },
            "required": [
              "latitude",
              "longitude"
            ],
            "description": "Where it happened, null when the tracker had no fresh position"
          }
        },
        "required": [
          "vehicleId",
          "kind",
          "peakAt",
          "peakMs2",
          "fromKmh",
          "toKmh",
          "place"
        ]
      },
      "HealthAlert": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "condition": {
            "type": "string",
            "enum": [
              "silent"
            ],
            "description": "What is wrong: silent, the tracker has not been heard for longer than it should"
          },
          "since": {
            "type": "string",
            "format": "date-time",
            "description": "Since when the condition holds"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the platform raised the alert"
          },
          "device": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "serial": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "serial"
            ],
            "description": "The tracker it is about"
          },
          "endedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it ended, null while it lasts"
          }
        },
        "required": [
          "id",
          "condition",
          "since",
          "startedAt",
          "device",
          "endedAt"
        ]
      },
      "HealthAlertEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "health-alert",
              "health-alert-ended"
            ]
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/HealthAlert"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "Installation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "deviceId": {
            "type": "string",
            "format": "uuid",
            "description": "The tracker"
          },
          "vehicleId": {
            "type": "string",
            "format": "uuid",
            "description": "The vehicle it was installed in"
          },
          "installedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was taken out, null while it is in"
          }
        },
        "required": [
          "id",
          "deviceId",
          "vehicleId",
          "installedAt",
          "removedAt"
        ]
      },
      "InstallationEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "device-installed",
              "device-removed"
            ]
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/Installation"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "Labels": {
        "type": "object",
        "properties": {
          "tr": {
            "type": "string",
            "description": "In Turkish"
          },
          "en": {
            "type": "string",
            "description": "In English"
          },
          "de": {
            "type": "string",
            "description": "In German"
          }
        },
        "required": [
          "tr",
          "en",
          "de"
        ]
      },
      "ListPage": {
        "type": "object",
        "properties": {
          "next": {
            "description": "An opaque cursor: send it as after for the next page, as it came; null on the last page, where the list ends",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "next"
        ]
      },
      "MeasureSeries": {
        "type": "object",
        "properties": {
          "signal": {
            "type": "string",
            "description": "The signal's name"
          },
          "kind": {
            "type": "string",
            "const": "measure"
          },
          "unit": {
            "type": "string",
            "description": "The unit of the values"
          },
          "paceSeconds": {
            "description": "The usual time between two readings of the signal, null when it was never read twice in a row",
            "type": [
              "number",
              "null"
            ]
          },
          "rare": {
            "type": "boolean",
            "description": "True for a signal read now and then rather than steadily, such as once a drive, best drawn as points"
          },
          "points": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "The start of the bucket"
                },
                "low": {
                  "type": "number",
                  "description": "The lowest value in the bucket"
                },
                "high": {
                  "type": "number",
                  "description": "The highest value in the bucket"
                },
                "mean": {
                  "type": "number",
                  "description": "The mean of the bucket, weighted by time"
                },
                "last": {
                  "type": "number",
                  "description": "The last value in the bucket"
                }
              },
              "required": [
                "at",
                "low",
                "high",
                "mean",
                "last"
              ]
            }
          }
        },
        "required": [
          "signal",
          "kind",
          "unit",
          "paceSeconds",
          "rare",
          "points"
        ]
      },
      "MeasureSignal": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The signal's name, as readings and series name it and as signals takes it"
          },
          "labels": {
            "description": "What the signal is, in each language",
            "$ref": "#/components/schemas/Labels"
          },
          "base": {
            "type": "boolean",
            "description": "True for a core reading every tracker gives wherever the vehicle offers it, false for one the vehicle's own setup adds"
          },
          "kind": {
            "type": "string",
            "const": "measure",
            "description": "A value that rises and falls, such as speed"
          },
          "unit": {
            "type": "string",
            "description": "The unit of its values, such as km/h, °C or L"
          },
          "decimals": {
            "type": "integer",
            "minimum": 0,
            "description": "The most digits after the decimal point a value has"
          }
        },
        "required": [
          "name",
          "labels",
          "base",
          "kind",
          "unit",
          "decimals"
        ]
      },
      "NewestPosition": {
        "type": "object",
        "properties": {
          "recordedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the tracker measured the position"
          },
          "latitude": {
            "type": "number",
            "description": "Degrees north, WGS 84"
          },
          "longitude": {
            "type": "number",
            "description": "Degrees east, WGS 84"
          },
          "accuracyM": {
            "description": "How far off the position may be, in metres",
            "type": [
              "number",
              "null"
            ]
          },
          "speedKmh": {
            "description": "Speed in km/h, null when unknown or without the vehicle data package",
            "type": [
              "number",
              "null"
            ]
          },
          "headingDeg": {
            "description": "Direction of travel in degrees clockwise from north",
            "type": [
              "number",
              "null"
            ]
          },
          "moving": {
            "type": "boolean",
            "description": "Whether the vehicle was moving at 5 km/h or more"
          },
          "silentAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the vehicle counts as silent if its tracker sends nothing more; once it has passed, the vehicle is silent"
          },
          "parkedSince": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Since when the vehicle has stood still after it last moved, null while it moves"
          }
        },
        "required": [
          "recordedAt",
          "latitude",
          "longitude",
          "accuracyM",
          "speedKmh",
          "headingDeg",
          "moving",
          "silentAt",
          "parkedSince"
        ]
      },
      "OpenFaults": {
        "type": "object",
        "properties": {
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "open": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "How many of its faults are still open"
          }
        },
        "required": [
          "vehicleId",
          "open"
        ]
      },
      "Place": {
        "type": "object",
        "properties": {
          "latitude": {
            "type": "number",
            "description": "Degrees north, WGS 84"
          },
          "longitude": {
            "type": "number",
            "description": "Degrees east, WGS 84"
          }
        },
        "required": [
          "latitude",
          "longitude"
        ]
      },
      "PlacedFuelEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "plate": {
            "type": "string"
          },
          "tank": {
            "type": "string",
            "enum": [
              "fuel",
              "def"
            ],
            "description": "The fuel tank, or def for AdBlue"
          },
          "kind": {
            "type": "string",
            "enum": [
              "drop",
              "refuel"
            ]
          },
          "during": {
            "type": "string",
            "enum": [
              "parked",
              "standing"
            ],
            "description": "Whether the vehicle was parked, or stood with the ignition on"
          },
          "levelBefore": {
            "type": "number",
            "description": "The tank's level before, in percent"
          },
          "levelAfter": {
            "type": "number",
            "description": "The tank's level after, in percent"
          },
          "litres": {
            "description": "How many litres that is, null without the tank's size",
            "type": [
              "number",
              "null"
            ]
          },
          "beforeAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the level before was read"
          },
          "afterAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the level after was read"
          },
          "detectedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the platform detected the drop or refuel"
          },
          "place": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "latitude": {
                "type": "number"
              },
              "longitude": {
                "type": "number"
              }
            },
            "required": [
              "latitude",
              "longitude"
            ],
            "description": "Where the vehicle stood when the level before was read, null when unknown or without position:read"
          }
        },
        "required": [
          "id",
          "vehicleId",
          "plate",
          "tank",
          "kind",
          "during",
          "levelBefore",
          "levelAfter",
          "litres",
          "beforeAt",
          "afterAt",
          "detectedAt",
          "place"
        ]
      },
      "PlatformThreshold": {
        "type": "object",
        "properties": {
          "by": {
            "type": "string",
            "const": "platform"
          },
          "comparison": {
            "type": "string",
            "enum": [
              "above",
              "below"
            ]
          }
        },
        "required": [
          "by",
          "comparison"
        ]
      },
      "PowerTakeOffReport": {
        "type": "object",
        "properties": {
          "vehicles": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "vehicleId": {
                  "type": "string",
                  "format": "uuid"
                },
                "plate": {
                  "type": "string"
                },
                "label": {
                  "description": "A name your organization gave the vehicle, if any",
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "drives": {
                  "type": "integer",
                  "description": "How many drives started in the period"
                },
                "completed": {
                  "type": "integer",
                  "description": "How many of them are complete, their figures worked out; the other figures sum only those, and the rest complete shortly after they end"
                },
                "drivingSeconds": {
                  "type": "number",
                  "description": "The time the vehicles moved, in seconds"
                },
                "distanceKm": {
                  "type": "number",
                  "description": "The distance driven, in km"
                },
                "powerTakeOffSeconds": {
                  "description": "The time the power take-off ran, in seconds",
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "powerTakeOffShare": {
                  "description": "The share of the drives' time the power take-off ran, from 0 to 1",
                  "type": [
                    "number",
                    "null"
                  ]
                }
              },
              "required": [
                "vehicleId",
                "plate",
                "label",
                "drives",
                "completed",
                "drivingSeconds",
                "distanceKm",
                "powerTakeOffSeconds",
                "powerTakeOffShare"
              ]
            }
          },
          "total": {
            "type": "object",
            "properties": {
              "drives": {
                "type": "integer",
                "description": "How many drives started in the period"
              },
              "completed": {
                "type": "integer",
                "description": "How many of them are complete, their figures worked out; the other figures sum only those, and the rest complete shortly after they end"
              },
              "drivingSeconds": {
                "type": "number",
                "description": "The time the vehicles moved, in seconds"
              },
              "distanceKm": {
                "type": "number",
                "description": "The distance driven, in km"
              },
              "powerTakeOffSeconds": {
                "description": "The time the power take-off ran, in seconds",
                "type": [
                  "number",
                  "null"
                ]
              },
              "powerTakeOffShare": {
                "description": "The share of the drives' time the power take-off ran, from 0 to 1",
                "type": [
                  "number",
                  "null"
                ]
              }
            },
            "required": [
              "drives",
              "completed",
              "drivingSeconds",
              "distanceKm",
              "powerTakeOffSeconds",
              "powerTakeOffShare"
            ]
          }
        },
        "required": [
          "vehicles",
          "total"
        ]
      },
      "ReadingSeries": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time",
            "description": "The first moment of the period"
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "description": "The last moment of the period"
          },
          "bucketSeconds": {
            "type": "number",
            "description": "How long each bucket is, in seconds"
          },
          "quiet": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "from": {
                  "type": "string",
                  "format": "date-time"
                },
                "to": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "required": [
                "from",
                "to"
              ]
            },
            "description": "Stretches in which the vehicle sent nothing, such as while it was parked, so a chart can leave them empty"
          },
          "series": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Series"
            },
            "description": "One series per signal, its points by bucket, oldest first"
          }
        },
        "required": [
          "from",
          "to",
          "bucketSeconds",
          "quiet",
          "series"
        ]
      },
      "Retention": {
        "type": "object",
        "properties": {
          "tier": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RetentionTier"
              },
              {
                "type": "null"
              }
            ],
            "description": "How long your data is kept now, and its price"
          },
          "next": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "tier": {
                "$ref": "#/components/schemas/RetentionTier"
              },
              "from": {
                "type": "string",
                "format": "date-time",
                "description": "When it takes over"
              }
            },
            "required": [
              "tier",
              "from"
            ],
            "description": "A shorter retention chosen to start later, null without one"
          }
        },
        "required": [
          "tier",
          "next"
        ]
      },
      "RetentionApplied": {
        "type": "object",
        "properties": {
          "months": {
            "type": "integer",
            "description": "How many months of data are kept"
          },
          "keptSince": {
            "type": "string",
            "format": "date-time",
            "description": "The moment from which your data is still kept; anything older is gone"
          }
        },
        "required": [
          "months",
          "keptSince"
        ]
      },
      "RetentionAppliedEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "const": "retention-applied"
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/RetentionApplied"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "RetentionChangedEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "const": "retention-changed"
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/Retention"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "RetentionTier": {
        "type": "object",
        "properties": {
          "months": {
            "type": "integer",
            "description": "How many months your data is kept"
          },
          "monthlyCents": {
            "type": "integer",
            "description": "Its price per vehicle and month, in euro cents"
          }
        },
        "required": [
          "months",
          "monthlyCents"
        ]
      },
      "Series": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/MeasureSeries"
          },
          {
            "$ref": "#/components/schemas/CounterSeries"
          },
          {
            "$ref": "#/components/schemas/StateSeries"
          },
          {
            "$ref": "#/components/schemas/TextSeries"
          }
        ],
        "discriminator": {
          "propertyName": "kind",
          "mapping": {
            "measure": "#/components/schemas/MeasureSeries",
            "counter": "#/components/schemas/CounterSeries",
            "state": "#/components/schemas/StateSeries",
            "text": "#/components/schemas/TextSeries"
          }
        }
      },
      "Signal": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/MeasureSignal"
          },
          {
            "$ref": "#/components/schemas/CounterSignal"
          },
          {
            "$ref": "#/components/schemas/StateSignal"
          },
          {
            "$ref": "#/components/schemas/TextSignal"
          }
        ],
        "discriminator": {
          "propertyName": "kind",
          "mapping": {
            "measure": "#/components/schemas/MeasureSignal",
            "counter": "#/components/schemas/CounterSignal",
            "state": "#/components/schemas/StateSignal",
            "text": "#/components/schemas/TextSignal"
          }
        }
      },
      "Silence": {
        "type": "object",
        "properties": {
          "vehicleId": {
            "type": "string",
            "format": "uuid"
          },
          "plate": {
            "type": "string"
          },
          "lastHeardAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the vehicle's tracker was last heard"
          }
        },
        "required": [
          "vehicleId",
          "plate",
          "lastHeardAt"
        ]
      },
      "StateSeries": {
        "type": "object",
        "properties": {
          "signal": {
            "type": "string",
            "description": "The signal's name"
          },
          "kind": {
            "type": "string",
            "const": "state"
          },
          "paceSeconds": {
            "description": "The usual time between two readings of the signal, null when it was never read twice in a row",
            "type": [
              "number",
              "null"
            ]
          },
          "rare": {
            "type": "boolean",
            "description": "True for a signal read now and then rather than steadily, such as once a drive, best drawn as points"
          },
          "points": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "The start of the bucket"
                },
                "state": {
                  "description": "The last state in the bucket by its name, or its number when the platform does not name it",
                  "type": [
                    "string",
                    "number"
                  ]
                }
              },
              "required": [
                "at",
                "state"
              ]
            }
          }
        },
        "required": [
          "signal",
          "kind",
          "paceSeconds",
          "rare",
          "points"
        ]
      },
      "StateSignal": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The signal's name, as readings and series name it and as signals takes it"
          },
          "labels": {
            "description": "What the signal is, in each language",
            "$ref": "#/components/schemas/Labels"
          },
          "base": {
            "type": "boolean",
            "description": "True for a core reading every tracker gives wherever the vehicle offers it, false for one the vehicle's own setup adds"
          },
          "kind": {
            "type": "string",
            "const": "state",
            "description": "One of a few named states"
          },
          "states": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "The state as a reading gives it"
                },
                "labels": {
                  "description": "What the state is, in each language",
                  "$ref": "#/components/schemas/Labels"
                }
              },
              "required": [
                "name",
                "labels"
              ]
            },
            "description": "Every state a reading may give"
          }
        },
        "required": [
          "name",
          "labels",
          "base",
          "kind",
          "states"
        ]
      },
      "TextSeries": {
        "type": "object",
        "properties": {
          "signal": {
            "type": "string",
            "description": "The signal's name"
          },
          "kind": {
            "type": "string",
            "const": "text"
          },
          "points": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "The start of the bucket"
                },
                "text": {
                  "type": "string",
                  "description": "The last text in the bucket"
                }
              },
              "required": [
                "at",
                "text"
              ]
            }
          }
        },
        "required": [
          "signal",
          "kind",
          "points"
        ]
      },
      "TextSignal": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The signal's name, as readings and series name it and as signals takes it"
          },
          "labels": {
            "description": "What the signal is, in each language",
            "$ref": "#/components/schemas/Labels"
          },
          "base": {
            "type": "boolean",
            "description": "True for a core reading every tracker gives wherever the vehicle offers it, false for one the vehicle's own setup adds"
          },
          "kind": {
            "type": "string",
            "const": "text",
            "description": "A short text"
          },
          "maxLength": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "The longest a text may be"
          }
        },
        "required": [
          "name",
          "labels",
          "base",
          "kind",
          "maxLength"
        ]
      },
      "ThresholdRange": {
        "type": "object",
        "properties": {
          "by": {
            "type": "string",
            "const": "customer"
          },
          "min": {
            "type": "number"
          },
          "max": {
            "type": "number"
          },
          "step": {
            "type": "number"
          }
        },
        "required": [
          "by",
          "min",
          "max",
          "step"
        ]
      },
      "TrackPoint": {
        "type": "object",
        "properties": {
          "recordedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the tracker measured the position"
          },
          "latitude": {
            "type": "number",
            "description": "Degrees north, WGS 84"
          },
          "longitude": {
            "type": "number",
            "description": "Degrees east, WGS 84"
          },
          "accuracyM": {
            "description": "How far off the position may be, in metres",
            "type": [
              "number",
              "null"
            ]
          },
          "speedKmh": {
            "description": "Speed in km/h, null when unknown or without the vehicle data package",
            "type": [
              "number",
              "null"
            ]
          },
          "headingDeg": {
            "description": "Direction of travel in degrees clockwise from north",
            "type": [
              "number",
              "null"
            ]
          },
          "moving": {
            "type": "boolean",
            "description": "Whether the vehicle was moving at 5 km/h or more"
          }
        },
        "required": [
          "recordedAt",
          "latitude",
          "longitude",
          "accuracyM",
          "speedKmh",
          "headingDeg",
          "moving"
        ]
      },
      "Vehicle": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "plate": {
            "type": "string"
          },
          "label": {
            "description": "A name your organization gave the vehicle, if any",
            "type": [
              "string",
              "null"
            ]
          },
          "vin": {
            "description": "The vehicle identification number",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "retired"
            ],
            "description": "active, or retired once the vehicle left your fleet, keeping its history"
          },
          "deviceId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The tracker installed in the vehicle now, null without one"
          },
          "kit": {
            "type": "boolean",
            "description": "Whether that tracker has a working vehicle kit, the add-on that reads the vehicle's own data such as fuel, engine and trouble codes"
          },
          "missingCapabilities": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "vehicle-kit"
              ],
              "description": "vehicle-kit: the tracker has no working vehicle kit, so features that read the vehicle's own data cannot work on it"
            },
            "description": "Hardware a feature your package has switched on needs and this vehicle's tracker lacks; empty when nothing is missing or no tracker is installed"
          }
        },
        "required": [
          "id",
          "plate",
          "label",
          "vin",
          "status",
          "deviceId",
          "kit",
          "missingCapabilities"
        ]
      },
      "VehicleAlertEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "const": "vehicle-alert"
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/AlertEpisode"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "VehicleEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "vehicle-added",
              "vehicle-changed"
            ]
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/Vehicle"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "VehicleFaultsEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "const": "vehicle-faults"
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "type": "object",
            "properties": {
              "vehicleId": {
                "type": "string",
                "format": "uuid"
              },
              "plate": {
                "type": "string"
              },
              "codes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "The trouble codes that came"
              }
            },
            "required": [
              "vehicleId",
              "plate",
              "codes"
            ],
            "description": "What happened, as that thing's own list gives it, and as it was when written"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "VehicleHeardAgainEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "const": "vehicle-heard-again"
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "type": "object",
            "properties": {
              "vehicleId": {
                "type": "string",
                "format": "uuid"
              },
              "plate": {
                "type": "string"
              },
              "lastHeardAt": {
                "type": "string",
                "format": "date-time",
                "description": "When the vehicle's tracker was last heard"
              },
              "heardAt": {
                "type": "string",
                "format": "date-time",
                "description": "When it was heard again"
              },
              "lostPower": {
                "type": "boolean",
                "description": "Whether the tracker lost its power while silent, such as when it was unplugged or the battery was disconnected or ran flat"
              }
            },
            "required": [
              "vehicleId",
              "plate",
              "lastHeardAt",
              "heardAt",
              "lostPower"
            ],
            "description": "What happened, as that thing's own list gives it, and as it was when written"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "VehicleReadings": {
        "type": "object",
        "properties": {
          "recordedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the newest of these values was measured"
          },
          "readings": {
            "type": "object",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "type": [
                "number",
                "string"
              ]
            },
            "description": "Each signal's value by its name: a number in the signal's unit, a state's name, or a text. A state the platform does not name comes as its number"
          }
        },
        "required": [
          "recordedAt",
          "readings"
        ]
      },
      "VehicleSilentEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "const": "vehicle-silent"
          },
          "cursor": {
            "type": "string",
            "description": "Send it as after to go on right after this entry"
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "description": "The organization the entry belongs to"
          },
          "vehicleId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The vehicle it is about, null for a tracker's health, a tracker not installed, a feature or a retention"
          },
          "writtenAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, the feed's order"
          },
          "subject": {
            "description": "What happened, as that thing's own list gives it, and as it was when written",
            "$ref": "#/components/schemas/Silence"
          }
        },
        "required": [
          "id",
          "kind",
          "cursor",
          "customerId",
          "vehicleId",
          "writtenAt",
          "subject"
        ]
      },
      "WebhookChallenge": {
        "type": "object",
        "properties": {
          "challenge": {
            "type": "string",
            "description": "A one-time value, new for every check"
          }
        },
        "required": [
          "challenge"
        ]
      },
      "WebhookChallengeAnswer": {
        "type": "object",
        "properties": {
          "answer": {
            "type": "string",
            "description": "The challenge's HMAC-SHA256 in base64, keyed with the base64-decoded secret after whsec_"
          }
        },
        "required": [
          "answer"
        ]
      }
    }
  },
  "x-error-codes": {
    "ALERT_RULE_LIMIT": {
      "status": 409,
      "meaning": "As many alert rules as may be on at once are on"
    },
    "CONFLICT": {
      "status": 409,
      "meaning": "The request conflicts with the present state"
    },
    "CURSOR_EXPIRED": {
      "status": 410,
      "meaning": "The cursor is too old to go on from; start the copy again, with from to reach further back"
    },
    "FEATURE_DISABLED": {
      "status": 403,
      "meaning": "The customer's package does not include the feature this needs, or an API key's organization does not hold the API package"
    },
    "FORBIDDEN": {
      "status": 403,
      "meaning": "The caller lacks a permission this needs, or the request came from another site"
    },
    "GEOFENCE_LIMIT": {
      "status": 409,
      "meaning": "The customer keeps as many geofences as it may"
    },
    "GEOFENCE_NAME_TAKEN": {
      "status": 409,
      "meaning": "Another geofence has this name"
    },
    "IDEMPOTENCY_KEY_REUSED": {
      "status": 409,
      "meaning": "This idempotency key was used before for a different request"
    },
    "INTERNAL_ERROR": {
      "status": 500,
      "meaning": "Something went wrong on our side"
    },
    "INVALID_REQUEST": {
      "status": 400,
      "meaning": "A path, query or body value is missing or malformed. Only a route that takes a body answers 413, for a body too large, or 415, for one of the wrong type"
    },
    "KEY_NOT_ALLOWED": {
      "status": 403,
      "meaning": "An API key reaches only the key routes, and this route and method is not one"
    },
    "METHOD_NOT_ALLOWED": {
      "status": 405,
      "meaning": "The path does not take this method; the Allow header names those it takes"
    },
    "NOT_FOUND": {
      "status": 404,
      "meaning": "Nothing in the caller's reach has this id"
    },
    "PASSWORD_CHANGE_REQUIRED": {
      "status": 403,
      "meaning": "A signed-in person must change their password before anything else. Only a person's session gets this; an API key never does, since a key has no password"
    },
    "PERSON_REQUIRED": {
      "status": 403,
      "meaning": "Only a signed-in person does this, such as an action that asks for a password; an API key never does"
    },
    "RATE_LIMITED": {
      "status": 429,
      "meaning": "Too many requests; the Retry-After header says when to try again"
    },
    "UNAUTHENTICATED": {
      "status": 401,
      "meaning": "No valid bearer token, session or live API key came with the request"
    }
  }
}
