{
  "swagger": "2.0",
  "info": {
    "version": "1.0.0",
    "title": "Connect Portal REST User API",
    "description": "## Welcome\n\nThis document serves as the comprehensive API interface description for our Connect Portal, detailing all endpoints and functionalities. Here, you will find general notes, extra information for internal use, and guidelines to get started using the API.\n\nIf you need help or support, you can reach us by phone at [+49 4193 / 870 900](tel:+494193870900) on weekdays from 09:00 to 18:00, or by email at [support@gps-watch.de](mailto:support@gps-watch.de).\n\nBest regards, GPS-WATCH GmbH"
  },
  "host": "connect.gps-watch.de",
  "basePath": "/api/rest",
  "schemes": [
    "https"
  ],
  "consumes": [
    "application/json"
  ],
  "produces": [
    "application/json"
  ],
  "securityDefinitions": {
    "api_key": {
      "type": "apiKey",
      "name": "X-API-Key",
      "in": "header",
      "description": "User API key provided by GPS-WATCH. Subuser object access requires both main-account ownership and the subuser's effective IMEI permissions. The Fleet Manager role grants the main account's full fleet."
    }
  },
  "security": [
    {
      "api_key": []
    }
  ],
  "paths": {
    "/": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "List available REST endpoints",
        "operationId": "getRestIndex",
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/auth/api-key": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Get the current user's API key with login credentials",
        "description": "Returns the user's own API key only when the account is active and API access is enabled for that user.",
        "operationId": "getApiKeyFromLogin",
        "security": [],
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/ApiKeyLoginRequest"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "API key response",
            "schema": {
              "allOf": [
                {
                  "$ref": "#/definitions/SuccessEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/definitions/ApiKeyLoginResponse"
                    }
                  }
                }
              ]
            }
          },
          "401": {
            "$ref": "#/responses/Error"
          },
          "403": {
            "$ref": "#/responses/Error"
          },
          "422": {
            "$ref": "#/responses/Error"
          },
          "429": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/profile": {
      "get": {
        "tags": [
          "User"
        ],
        "summary": "Get current user profile",
        "operationId": "getProfile",
        "responses": {
          "200": {
            "description": "Current user profile",
            "schema": {
              "allOf": [
                {
                  "$ref": "#/definitions/SuccessEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/definitions/Profile"
                    }
                  }
                }
              ]
            }
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      },
      "patch": {
        "tags": [
          "User"
        ],
        "summary": "Update current user profile",
        "operationId": "updateProfile",
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/ProfileUpdate"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          },
          "422": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/objects": {
      "get": {
        "tags": [
          "Objects"
        ],
        "summary": "List objects available to the current user",
        "operationId": "listObjects",
        "responses": {
          "200": {
            "description": "Object list",
            "schema": {
              "allOf": [
                {
                  "$ref": "#/definitions/SuccessEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/definitions/Object"
                      }
                    }
                  }
                }
              ]
            }
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/objects/groups": {
      "get": {
        "tags": [
          "Objects"
        ],
        "summary": "List object groups",
        "operationId": "listObjectGroups",
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/objects/locations": {
      "get": {
        "tags": [
          "Objects"
        ],
        "summary": "Get latest object locations",
        "operationId": "listObjectLocations",
        "parameters": [
          {
            "name": "imeis",
            "in": "query",
            "required": false,
            "type": "string",
            "default": "*",
            "description": "`*` for all accessible objects or semicolon separated IMEIs."
          }
        ],
        "responses": {
          "200": {
            "description": "Locations keyed by IMEI",
            "schema": {
              "allOf": [
                {
                  "$ref": "#/definitions/SuccessEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "additionalProperties": {
                        "$ref": "#/definitions/ObjectLocation"
                      }
                    }
                  }
                }
              ]
            }
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/objects/{imei}/location": {
      "get": {
        "tags": [
          "Objects"
        ],
        "summary": "Get latest location for one object",
        "operationId": "getObjectLocation",
        "parameters": [
          {
            "name": "imei",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "403": {
            "$ref": "#/responses/Error"
          },
          "404": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/markers": {
      "get": {
        "tags": [
          "Map"
        ],
        "summary": "List user markers",
        "operationId": "listMarkers",
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          }
        }
      }
    },
    "/routes": {
      "get": {
        "tags": [
          "Map"
        ],
        "summary": "List user routes",
        "operationId": "listRoutes",
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          }
        }
      }
    },
    "/zones": {
      "get": {
        "tags": [
          "Map"
        ],
        "summary": "List user zones",
        "operationId": "listZones",
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          }
        }
      }
    },
    "/zones/limit": {
      "get": {
        "tags": [
          "Map"
        ],
        "summary": "Get current zone limit and usage",
        "description": "Returns the authenticated user's effective zone limit, total stored zone count and remaining capacity. The CPanel account setting overrides the server default; an empty account setting uses the server default. Zero means no new zones are allowed. Values are read on each request and creation uses the same limit. Remaining capacity is a snapshot, not a reservation.",
        "operationId": "getZoneLimit",
        "responses": {
          "200": {
            "description": "Current zone capacity",
            "schema": {
              "allOf": [
                {
                  "$ref": "#/definitions/SuccessEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/definitions/ZoneLimitStatus"
                    }
                  }
                }
              ]
            },
            "examples": {
              "application/json": {
                "success": true,
                "data": {
                  "limit": 500,
                  "count": 100,
                  "remaining": 400
                }
              }
            }
          },
          "401": {
            "$ref": "#/responses/Error"
          },
          "429": {
            "$ref": "#/responses/Error"
          },
          "500": {
            "description": "Zone limit or count could not be loaded (zone_limit_unavailable)",
            "schema": {
              "$ref": "#/definitions/ErrorEnvelope"
            }
          }
        }
      }
    },
    "/support": {
      "post": {
        "tags": [
          "Support"
        ],
        "summary": "Send support request",
        "operationId": "sendSupport",
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "description": "Support request payload.",
            "schema": {
              "type": "object",
              "required": [
                "category",
                "message"
              ],
              "properties": {
                "category": {
                  "type": "string",
                  "enum": [
                    "issue",
                    "help",
                    "question"
                  ],
                  "description": "Support request category. `issue` = Fehler, `help` = Hilfe, `question` = Frage."
                },
                "imei": {
                  "type": "string",
                  "description": "Optional object IMEI. Leave empty or omit to reference all devices."
                },
                "message": {
                  "type": "string",
                  "description": "Support message text."
                }
              },
              "example": {
                "category": "issue",
                "imei": "123456789012345",
                "message": "Das Fahrzeug sendet seit gestern keine Positionen mehr."
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Support request was sent.",
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": true
                }
              }
            },
            "examples": {
              "application/json": {
                "success": true
              }
            }
          },
          "401": {
            "$ref": "#/responses/Error"
          },
          "422": {
            "$ref": "#/responses/Error"
          }
        },
        "description": "Sends a support request to GPS-WATCH support for the authenticated user. The request can optionally reference one object by IMEI. If no IMEI is provided, the request applies to all devices."
      }
    },
    "/subaccounts": {
      "post": {
        "tags": [
          "Subaccounts"
        ],
        "summary": "Create a subaccount",
        "operationId": "createSubaccount",
        "description": "Creates an employee subaccount for the authenticated manager account. The manager must have the subaccount privilege enabled. Device, marker, route, zone and module permissions are capped to the manager account's own permissions.",
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/SubaccountCreate"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Created subaccount",
            "schema": {
              "$ref": "#/definitions/SubaccountResponse"
            }
          },
          "401": {
            "$ref": "#/responses/Error"
          },
          "422": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/subaccounts/{subaccount_id}": {
      "patch": {
        "tags": [
          "Subaccounts"
        ],
        "summary": "Update a subaccount",
        "operationId": "updateSubaccount",
        "description": "Updates an employee subaccount owned by the authenticated manager account. Omitted fields stay unchanged; empty permission arrays remove the corresponding permissions.",
        "parameters": [
          {
            "name": "subaccount_id",
            "in": "path",
            "required": true,
            "type": "integer"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/SubaccountUpdate"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated subaccount",
            "schema": {
              "$ref": "#/definitions/SubaccountUpdateResponse"
            }
          },
          "401": {
            "$ref": "#/responses/Error"
          },
          "422": {
            "$ref": "#/responses/Error"
          }
        }
      },
      "post": {
        "tags": [
          "Subaccounts"
        ],
        "summary": "Update a subaccount",
        "operationId": "postUpdateSubaccount",
        "description": "Compatibility alias for PATCH /subaccounts/{subaccount_id}.",
        "parameters": [
          {
            "name": "subaccount_id",
            "in": "path",
            "required": true,
            "type": "integer"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/SubaccountUpdate"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated subaccount",
            "schema": {
              "$ref": "#/definitions/SubaccountUpdateResponse"
            }
          },
          "401": {
            "$ref": "#/responses/Error"
          },
          "422": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/reports/generated": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "List generated reports",
        "operationId": "listGeneratedReports",
        "parameters": [],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/reports/{report_id}/content": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Get generated report content",
        "operationId": "getGeneratedReportContent",
        "parameters": [
          {
            "name": "report_id",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/reports/{report_id}/download": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Download generated report",
        "operationId": "downloadGeneratedReport",
        "parameters": [
          {
            "name": "report_id",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "Report file download"
          },
          "401": {
            "$ref": "#/responses/Error"
          },
          "404": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/travelsheet/objects": {
      "get": {
        "tags": [
          "Travelsheet"
        ],
        "summary": "List Travelsheet objects",
        "operationId": "listTravelsheetObjects",
        "parameters": [],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/travelsheet/month-options": {
      "get": {
        "tags": [
          "Travelsheet"
        ],
        "summary": "List available Travelsheet months",
        "operationId": "listTravelsheetMonthOptions",
        "parameters": [
          {
            "name": "imei",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Object IMEI"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/travelsheet/month-overview": {
      "get": {
        "tags": [
          "Travelsheet"
        ],
        "summary": "Get Travelsheet month overview",
        "operationId": "getTravelsheetMonthOverview",
        "parameters": [
          {
            "name": "imei",
            "in": "query",
            "required": true,
            "type": "string"
          },
          {
            "name": "month",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Month in YYYY-MM format"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/travelsheet/day-trips": {
      "get": {
        "tags": [
          "Travelsheet"
        ],
        "summary": "Get Travelsheet trips for one day",
        "operationId": "getTravelsheetDayTrips",
        "parameters": [
          {
            "name": "imei",
            "in": "query",
            "required": true,
            "type": "string"
          },
          {
            "name": "date",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Date in YYYY-MM-DD format"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/travelsheet/trips": {
      "post": {
        "tags": [
          "Travelsheet"
        ],
        "summary": "Save Travelsheet trip",
        "operationId": "saveTravelsheetTrip",
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "description": "Travelsheet trip payload.",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      },
      "patch": {
        "tags": [
          "Travelsheet"
        ],
        "summary": "Update Travelsheet trip",
        "operationId": "updateTravelsheetTrip",
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "description": "Travelsheet trip payload.",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/worktime/status": {
      "get": {
        "tags": [
          "Worktime"
        ],
        "summary": "Get current worktime status",
        "operationId": "getWorktimeStatus",
        "parameters": [],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/worktime/actions": {
      "post": {
        "tags": [
          "Worktime"
        ],
        "summary": "Create worktime action",
        "operationId": "createWorktimeAction",
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "properties": {
                "action": {
                  "type": "string",
                  "enum": [
                    "start",
                    "pause",
                    "resume",
                    "end"
                  ]
                }
              },
              "required": [
                "action"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/tasks/open": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "summary": "List open tasks",
        "operationId": "listOpenTasks",
        "parameters": [
          {
            "name": "imei",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Object IMEI"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/tasks/{task_id}/status": {
      "patch": {
        "tags": [
          "Tasks"
        ],
        "summary": "Update task status",
        "operationId": "updateTaskStatus",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "properties": {
                "status": {
                  "type": "integer",
                  "description": "Task status. Allowed values: 1, 2 or 3."
                }
              },
              "required": [
                "status"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/tasks": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "summary": "List tasks",
        "operationId": "listTasks",
        "parameters": [
          {
            "name": "imei",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Object IMEI or *"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Date/time from"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Date/time to"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/chat/messages": {
      "get": {
        "tags": [
          "Chat"
        ],
        "summary": "List chat messages",
        "operationId": "listChatMessages",
        "parameters": [
          {
            "name": "imei",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Object IMEI"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "type": "integer",
            "description": "Number of messages, 1-200"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      },
      "post": {
        "tags": [
          "Chat"
        ],
        "summary": "Send chat message",
        "operationId": "sendChatMessage",
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "properties": {
                "imei": {
                  "type": "string"
                },
                "msg": {
                  "type": "string"
                }
              },
              "required": [
                "imei",
                "msg"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/objects/{imei}/commands": {
      "get": {
        "tags": [
          "Commands"
        ],
        "summary": "List object commands",
        "operationId": "listObjectCommands",
        "parameters": [
          {
            "name": "imei",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/objects/{imei}/commands/gprs": {
      "post": {
        "tags": [
          "Commands"
        ],
        "summary": "Send GPRS command",
        "operationId": "sendObjectGprsCommand",
        "parameters": [
          {
            "name": "imei",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                },
                "cmd": {
                  "type": "string"
                }
              },
              "required": [
                "name",
                "type",
                "cmd"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/objects/{imei}/commands/sms": {
      "post": {
        "tags": [
          "Commands"
        ],
        "summary": "Send SMS command",
        "operationId": "sendObjectSmsCommand",
        "parameters": [
          {
            "name": "imei",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "cmd": {
                  "type": "string"
                }
              },
              "required": [
                "name",
                "cmd"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/command-templates": {
      "get": {
        "tags": [
          "Commands"
        ],
        "summary": "List command templates",
        "operationId": "listCommandTemplates",
        "parameters": [],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/command-templates/{template_id}/send": {
      "post": {
        "tags": [
          "Commands"
        ],
        "summary": "Send command template",
        "operationId": "sendCommandTemplate",
        "parameters": [
          {
            "name": "template_id",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "properties": {
                "imeis": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "required": [
                "imeis"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/objects/{imei}/details": {
      "post": {
        "tags": [
          "Objects"
        ],
        "summary": "Update object details",
        "description": "Requires object ownership and the authenticated account's current CPanel object-edit permission (obj_edit=true). A denied edit retains the legacy plain-text response ERROR: object edit access denied. Demo accounts are read-only.",
        "operationId": "updateObjectDetails",
        "parameters": [
          {
            "name": "imei",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "plate_number": {
                  "type": "string"
                },
                "model": {
                  "type": "string"
                },
                "vin": {
                  "type": "string"
                },
                "odometer": {
                  "type": "string"
                }
              },
              "required": [
                "name"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      },
      "patch": {
        "tags": [
          "Objects"
        ],
        "summary": "Patch object details",
        "description": "Requires object ownership and the authenticated account's current CPanel object-edit permission (obj_edit=true). A denied edit retains the legacy plain-text response ERROR: object edit access denied. Demo accounts are read-only.",
        "operationId": "patchObjectDetails",
        "parameters": [
          {
            "name": "imei",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "plate_number": {
                  "type": "string"
                },
                "model": {
                  "type": "string"
                },
                "vin": {
                  "type": "string"
                },
                "odometer": {
                  "type": "string"
                }
              },
              "required": [
                "name"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/objects/{imei}/route": {
      "get": {
        "tags": [
          "History"
        ],
        "summary": "Get object route",
        "operationId": "getObjectRoute",
        "parameters": [
          {
            "name": "imei",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Date/time from"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Date/time to"
          },
          {
            "name": "min_stop_duration",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Stop threshold in minutes. A stop separates drives only when its duration is strictly greater than this threshold (duration > min_stop_duration * 60 seconds). Existing object stop-detection settings still apply."
          },
          {
            "name": "summary",
            "in": "query",
            "required": false,
            "type": "boolean",
            "default": false,
            "description": "Omit route points, events and their point-array indices while retaining stops, drives and available totals. Accepted values: true, false, 1, 0 (case-insensitive). Omitted or empty values (summary=) use the default false. Arrays and other values return HTTP 422."
          },
          {
            "name": "include_zones",
            "in": "query",
            "required": false,
            "type": "boolean",
            "default": false,
            "description": "Add current authorized zone labels to stops and drive endpoints. Accepted values: true, false, 1, 0 (case-insensitive). Omitted or empty values (include_zones=) use the default false. Arrays and other values return HTTP 422. Independent of summary; omitted, empty or false adds no zone fields."
          }
        ],
        "responses": {
          "200": {
            "description": "Legacy route JSON directly at the response root. Available totals and legacy fields vary with the returned data; route/events and point indices are absent in summary mode. Zone fields exist only with include_zones=true. If the include_zones zone query fails, the Legacy response is plain text: ERROR: route zones could not be loaded.",
            "schema": {
              "$ref": "#/definitions/ObjectRouteResponse"
            },
            "examples": {
              "application/json": {
                "stops": [
                  {
                    "lat": 48.775,
                    "lng": 9.18,
                    "dt_start": "2026-10-14 08:50:00",
                    "dt_end": "2026-10-14 09:00:00",
                    "duration": "10 min",
                    "address": "Marktplatz 1, 70173 Stuttgart",
                    "zones": [
                      {
                        "zone_id": "123",
                        "name": "Apotheke Am Markt"
                      }
                    ]
                  }
                ],
                "drives": [
                  {
                    "dt_start_s": "2026-10-14 08:50:00",
                    "dt_start": "2026-10-14 09:00:00",
                    "dt_end": "2026-10-14 09:15:00",
                    "start_lat": 48.775,
                    "start_lng": 9.18,
                    "end_lat": 48.78,
                    "end_lng": 9.2,
                    "start_address": "Marktplatz 1, 70173 Stuttgart",
                    "end_address": "Example destination, Stuttgart",
                    "duration": "15 min",
                    "route_length": 4.2,
                    "top_speed": 45,
                    "avg_speed": 16,
                    "start_zones": [
                      {
                        "zone_id": "123",
                        "name": "Apotheke Am Markt"
                      }
                    ],
                    "end_zones": []
                  }
                ],
                "route_length": 4.2,
                "stops_duration": "10 min",
                "drives_duration": "15 min"
              }
            },
            "x-examples": {
              "full": {
                "route": [
                  [
                    "2026-10-14 08:50:00",
                    48.775,
                    9.18,
                    260,
                    90,
                    0,
                    {},
                    false
                  ],
                  [
                    "2026-10-14 09:00:00",
                    48.775,
                    9.18,
                    260,
                    90,
                    0,
                    {},
                    false
                  ],
                  [
                    "2026-10-14 09:15:00",
                    48.78,
                    9.2,
                    260,
                    90,
                    45,
                    {},
                    false
                  ]
                ],
                "stops": [
                  {
                    "id_start": 0,
                    "id_end": 1,
                    "lat": 48.775,
                    "lng": 9.18,
                    "dt_start": "2026-10-14 08:50:00",
                    "dt_end": "2026-10-14 09:00:00",
                    "duration": "10 min",
                    "address": "Marktplatz 1, 70173 Stuttgart"
                  }
                ],
                "drives": [
                  {
                    "id_start_s": 0,
                    "id_start": 1,
                    "id_end": 2,
                    "dt_start_s": "2026-10-14 08:50:00",
                    "dt_start": "2026-10-14 09:00:00",
                    "dt_end": "2026-10-14 09:15:00",
                    "start_lat": 48.775,
                    "start_lng": 9.18,
                    "end_lat": 48.78,
                    "end_lng": 9.2,
                    "start_address": "Marktplatz 1, 70173 Stuttgart",
                    "end_address": "Example destination, Stuttgart",
                    "duration": "15 min",
                    "route_length": 4.2,
                    "top_speed": 45,
                    "avg_speed": 16
                  }
                ],
                "events": [],
                "route_length": 4.2,
                "stops_duration": "10 min",
                "drives_duration": "15 min"
              },
              "summary": {
                "stops": [
                  {
                    "lat": 48.775,
                    "lng": 9.18,
                    "dt_start": "2026-10-14 08:50:00",
                    "dt_end": "2026-10-14 09:00:00",
                    "duration": "10 min",
                    "address": "Marktplatz 1, 70173 Stuttgart"
                  }
                ],
                "drives": [
                  {
                    "dt_start_s": "2026-10-14 08:50:00",
                    "dt_start": "2026-10-14 09:00:00",
                    "dt_end": "2026-10-14 09:15:00",
                    "start_lat": 48.775,
                    "start_lng": 9.18,
                    "end_lat": 48.78,
                    "end_lng": 9.2,
                    "start_address": "Marktplatz 1, 70173 Stuttgart",
                    "end_address": "Example destination, Stuttgart",
                    "duration": "15 min",
                    "route_length": 4.2,
                    "top_speed": 45,
                    "avg_speed": 16
                  }
                ],
                "route_length": 4.2,
                "stops_duration": "10 min",
                "drives_duration": "15 min"
              },
              "summary_with_zones": {
                "stops": [
                  {
                    "lat": 48.775,
                    "lng": 9.18,
                    "dt_start": "2026-10-14 08:50:00",
                    "dt_end": "2026-10-14 09:00:00",
                    "duration": "10 min",
                    "address": "Marktplatz 1, 70173 Stuttgart",
                    "zones": [
                      {
                        "zone_id": "123",
                        "name": "Apotheke Am Markt"
                      }
                    ]
                  }
                ],
                "drives": [
                  {
                    "dt_start_s": "2026-10-14 08:50:00",
                    "dt_start": "2026-10-14 09:00:00",
                    "dt_end": "2026-10-14 09:15:00",
                    "start_lat": 48.775,
                    "start_lng": 9.18,
                    "end_lat": 48.78,
                    "end_lng": 9.2,
                    "start_address": "Marktplatz 1, 70173 Stuttgart",
                    "end_address": "Example destination, Stuttgart",
                    "duration": "15 min",
                    "route_length": 4.2,
                    "top_speed": 45,
                    "avg_speed": 16,
                    "start_zones": [
                      {
                        "zone_id": "123",
                        "name": "Apotheke Am Markt"
                      }
                    ],
                    "end_zones": []
                  }
                ],
                "route_length": 4.2,
                "stops_duration": "10 min",
                "drives_duration": "15 min"
              },
              "full_with_zones": {
                "route": [
                  [
                    "2026-10-14 08:50:00",
                    48.775,
                    9.18,
                    260,
                    90,
                    0,
                    {},
                    false
                  ],
                  [
                    "2026-10-14 09:00:00",
                    48.775,
                    9.18,
                    260,
                    90,
                    0,
                    {},
                    false
                  ],
                  [
                    "2026-10-14 09:15:00",
                    48.78,
                    9.2,
                    260,
                    90,
                    45,
                    {},
                    false
                  ]
                ],
                "stops": [
                  {
                    "id_start": 0,
                    "id_end": 1,
                    "lat": 48.775,
                    "lng": 9.18,
                    "dt_start": "2026-10-14 08:50:00",
                    "dt_end": "2026-10-14 09:00:00",
                    "duration": "10 min",
                    "address": "Marktplatz 1, 70173 Stuttgart",
                    "zones": [
                      {
                        "zone_id": "123",
                        "name": "Apotheke Am Markt"
                      }
                    ]
                  }
                ],
                "drives": [
                  {
                    "id_start_s": 0,
                    "id_start": 1,
                    "id_end": 2,
                    "dt_start_s": "2026-10-14 08:50:00",
                    "dt_start": "2026-10-14 09:00:00",
                    "dt_end": "2026-10-14 09:15:00",
                    "start_lat": 48.775,
                    "start_lng": 9.18,
                    "end_lat": 48.78,
                    "end_lng": 9.2,
                    "start_address": "Marktplatz 1, 70173 Stuttgart",
                    "end_address": "Example destination, Stuttgart",
                    "duration": "15 min",
                    "route_length": 4.2,
                    "top_speed": 45,
                    "avg_speed": 16,
                    "start_zones": [
                      {
                        "zone_id": "123",
                        "name": "Apotheke Am Markt"
                      }
                    ],
                    "end_zones": []
                  }
                ],
                "events": [],
                "route_length": 4.2,
                "stops_duration": "10 min",
                "drives_duration": "15 min"
              }
            }
          },
          "401": {
            "$ref": "#/responses/Error"
          },
          "422": {
            "description": "Invalid summary or include_zones value. Each option accepts true, false, 1 or 0 (case-insensitive); omitted or empty values use false. Arrays and other values are rejected.",
            "schema": {
              "$ref": "#/definitions/ErrorEnvelope"
            }
          }
        },
        "description": "Returns the existing Legacy route JSON directly, without a success/data envelope. By default, the response includes the full route point array, events, stops, drives and available totals, with no zone-label fields.\n\nBoth options accept true, false, 1 or 0 (case-insensitive). An omitted or empty query value (summary= or include_zones=) means false; arrays and other values return HTTP 422.\n\nSet summary=true to return stops, drives and the existing available totals without route or events. Point-array indices are also omitted: stop id_start/id_end and drive id_start_s/id_start/id_end. Times, durations, distances and other values retain the account timezone and units. Full points are still loaded internally for the same calculations; summary reduces the response size and does not change the existing raw-data limits.\n\nSet include_zones=true to add zones to each stop and start_zones/end_zones to each drive. Each field is an array of {zone_id, name}; overlapping zones can produce multiple entries, and no match produces []. These are current zone definitions belonging to the object owner account, restricted to the currently authorized zone permissions for subusers; they are not historical zone snapshots. Malformed stored zone polygons are skipped; valid matching zones remain in the result. If the zone query fails, the endpoint retains the Legacy HTTP 200 plain-text response ERROR: route zones could not be loaded, without a success/data envelope. Existing address/start_address/end_address fields remain separate street addresses. include_zones is independent of summary and can also enrich the full response.\n\nExamples: /objects/{imei}/route?from=2026-10-14%2008:50:00&to=2026-10-14%2009:15:00&min_stop_duration=5 (full response); append &summary=true for stops/drives/totals only, &summary=true&include_zones=true for a summary with zone labels, or &include_zones=true for full route points with zone labels. The response examples use fictional values."
      }
    },
    "/objects/{imei}/messages": {
      "get": {
        "tags": [
          "History"
        ],
        "summary": "Get object messages",
        "operationId": "getObjectMessages",
        "parameters": [
          {
            "name": "imei",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "type": "string"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/objects/{imei}/events": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "Get object events",
        "operationId": "getObjectEvents",
        "parameters": [
          {
            "name": "imei",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "type": "string"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/events/latest": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "Get latest events",
        "operationId": "getLatestEvents",
        "parameters": [
          {
            "name": "imei",
            "in": "query",
            "required": false,
            "type": "string",
            "description": "Object IMEI or * for all accessible objects"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/events/latest-7d": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "Get latest events from last 7 days",
        "operationId": "getLatestEvents7d",
        "parameters": [
          {
            "name": "imei",
            "in": "query",
            "required": false,
            "type": "string",
            "description": "Object IMEI or * for all accessible objects"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/zones/circle": {
      "post": {
        "tags": [
          "Map"
        ],
        "summary": "Create circle zone",
        "description": "Uses the CPanel account zone limit, or the server default when the account setting is empty. GET /zones/limit returns the current limit and usage. At the limit, the legacy response remains HTTP 200 with plain text ERROR_ZONE_LIMIT.",
        "operationId": "createCircleZone",
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "description": "Circle zone payload.",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/zones/{zone_id}/circle": {
      "post": {
        "tags": [
          "Map"
        ],
        "summary": "Save circle zone",
        "operationId": "saveCircleZone",
        "parameters": [
          {
            "name": "zone_id",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "description": "Circle zone payload.",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      },
      "patch": {
        "tags": [
          "Map"
        ],
        "summary": "Update circle zone",
        "operationId": "updateCircleZone",
        "parameters": [
          {
            "name": "zone_id",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "description": "Circle zone payload.",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/zones/{zone_id}": {
      "delete": {
        "tags": [
          "Map"
        ],
        "summary": "Delete zone",
        "operationId": "deleteZone",
        "parameters": [
          {
            "name": "zone_id",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/event-rules": {
      "get": {
        "tags": [
          "Event Rules"
        ],
        "summary": "List event rules",
        "operationId": "listEventRules",
        "parameters": [],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      },
      "post": {
        "tags": [
          "Event Rules"
        ],
        "summary": "Create event rule",
        "operationId": "createEventRule",
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "description": "Event rule payload. Use object_selection_type='imei' with imeis, or object_selection_type='group' with object_group_ids/group_ids.",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/event-rules/{rule_id}": {
      "patch": {
        "tags": [
          "Event Rules"
        ],
        "summary": "Update event rule",
        "operationId": "updateEventRule",
        "parameters": [
          {
            "name": "rule_id",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "description": "Event rule payload. Use object_selection_type='imei' with imeis, or object_selection_type='group' with object_group_ids/group_ids.",
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      },
      "delete": {
        "tags": [
          "Event Rules"
        ],
        "summary": "Delete event rule",
        "operationId": "deleteEventRule",
        "parameters": [
          {
            "name": "rule_id",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/rilogbook": {
      "get": {
        "tags": [
          "Maintenance"
        ],
        "summary": "List RIL logbook entries",
        "operationId": "listRilogbook",
        "parameters": [
          {
            "name": "imei",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Object IMEI, semicolon separated IMEIs, or *"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Date/time from"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Date/time to"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/dtc": {
      "get": {
        "tags": [
          "Maintenance"
        ],
        "summary": "List DTC entries",
        "operationId": "listDtc",
        "parameters": [
          {
            "name": "imei",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Object IMEI, semicolon separated IMEIs, or *"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Date/time from"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Date/time to"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/maintenance": {
      "get": {
        "tags": [
          "Maintenance"
        ],
        "summary": "List maintenance entries",
        "operationId": "listMaintenance",
        "parameters": [
          {
            "name": "imei",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Object IMEI, semicolon separated IMEIs, or *"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    },
    "/expenses": {
      "get": {
        "tags": [
          "Maintenance"
        ],
        "summary": "List expenses",
        "operationId": "listExpenses",
        "parameters": [
          {
            "name": "imei",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Object IMEI, semicolon separated IMEIs, or *"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Date/time from"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "type": "string",
            "description": "Date/time to"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/responses/Success"
          },
          "401": {
            "$ref": "#/responses/Error"
          }
        }
      }
    }
  },
  "definitions": {
    "SuccessEnvelope": {
      "type": "object",
      "required": [
        "success",
        "data"
      ],
      "properties": {
        "success": {
          "type": "boolean",
          "example": true
        },
        "data": {
          "type": "object"
        }
      }
    },
    "ErrorEnvelope": {
      "type": "object",
      "required": [
        "success",
        "error"
      ],
      "properties": {
        "success": {
          "type": "boolean",
          "example": false
        },
        "error": {
          "type": "object",
          "properties": {
            "code": {
              "type": "string",
              "example": "invalid_api_key"
            },
            "message": {
              "type": "string",
              "example": "Invalid API key."
            }
          }
        }
      }
    },
    "ZoneLimitStatus": {
      "type": "object",
      "required": [
        "limit",
        "count",
        "remaining"
      ],
      "properties": {
        "limit": {
          "type": "integer",
          "minimum": 0,
          "description": "Effective account zone limit; zero disallows creation.",
          "example": 500
        },
        "count": {
          "type": "integer",
          "minimum": 0,
          "description": "Total stored zones for the authenticated user, including all zone shapes.",
          "example": 100
        },
        "remaining": {
          "type": "integer",
          "minimum": 0,
          "description": "max(0, limit - count); a snapshot, not reserved capacity.",
          "example": 400
        }
      }
    },
    "ApiKeyLoginRequest": {
      "type": "object",
      "required": [
        "login",
        "password"
      ],
      "properties": {
        "login": {
          "type": "string",
          "description": "Username or account e-mail address."
        },
        "password": {
          "type": "string",
          "format": "password"
        }
      },
      "example": {
        "login": "user@example.com",
        "password": "secret"
      }
    },
    "ApiKeyLoginResponse": {
      "type": "object",
      "properties": {
        "api_key": {
          "type": "string"
        },
        "username": {
          "type": "string"
        }
      }
    },
    "Profile": {
      "type": "object",
      "properties": {
        "username": {
          "type": "string"
        },
        "email": {
          "type": "string"
        },
        "owner_email": {
          "type": "string"
        },
        "privileges": {
          "type": "string"
        },
        "modules": {
          "type": "object",
          "properties": {
            "worktime": {
              "type": "boolean"
            }
          }
        },
        "manager_id": {
          "type": "string"
        },
        "info": {
          "$ref": "#/definitions/ProfileInfo"
        }
      }
    },
    "ProfileInfo": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "company": {
          "type": "string"
        },
        "address": {
          "type": "string"
        },
        "post_code": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "country": {
          "type": "string"
        },
        "state": {
          "type": "string"
        },
        "phone1": {
          "type": "string"
        },
        "phone2": {
          "type": "string"
        }
      }
    },
    "ProfileUpdate": {
      "type": "object",
      "properties": {
        "email": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "company": {
          "type": "string"
        },
        "address": {
          "type": "string"
        },
        "post_code": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "country": {
          "type": "string"
        },
        "state": {
          "type": "string"
        },
        "phone1": {
          "type": "string"
        },
        "phone2": {
          "type": "string"
        },
        "current_password": {
          "type": "string"
        },
        "new_password": {
          "type": "string"
        }
      }
    },
    "SubaccountModules": {
      "type": "object",
      "description": "Subaccount module privileges. Missing keys default to false on create and stay unchanged on update.",
      "properties": {
        "dashboard": {
          "type": "boolean"
        },
        "history": {
          "type": "boolean"
        },
        "reports": {
          "type": "boolean"
        },
        "travelsheet": {
          "type": "boolean"
        },
        "worktime": {
          "type": "boolean"
        },
        "tachograph": {
          "type": "boolean"
        },
        "tasks": {
          "type": "boolean"
        },
        "rilogbook": {
          "type": "boolean"
        },
        "dtc": {
          "type": "boolean"
        },
        "maintenance": {
          "type": "boolean"
        },
        "expenses": {
          "type": "boolean"
        },
        "object_control": {
          "type": "boolean"
        },
        "image_gallery": {
          "type": "boolean"
        },
        "chat": {
          "type": "boolean"
        }
      }
    },
    "SubaccountCreate": {
      "type": "object",
      "required": [
        "email"
      ],
      "properties": {
        "email": {
          "type": "string",
          "format": "email"
        },
        "username": {
          "type": "string",
          "description": "Optional login username. When omitted, the email is used by the legacy account creation flow."
        },
        "password": {
          "type": "string",
          "description": "Required unless send_credentials is true. Must be at least 6 characters and contain no whitespace."
        },
        "imeis": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Object IMEIs to assign to the subaccount."
        },
        "modules": {
          "$ref": "#/definitions/SubaccountModules"
        },
        "send_credentials": {
          "type": "boolean",
          "default": false,
          "description": "When true, the legacy API may generate and send credentials instead of requiring a password."
        },
        "active": {
          "type": "boolean",
          "default": true
        },
        "account_expire": {
          "type": "boolean",
          "default": false
        },
        "account_expire_dt": {
          "type": "string",
          "format": "date"
        },
        "marker_ids": {
          "type": "array",
          "items": {
            "type": "integer"
          }
        },
        "route_ids": {
          "type": "array",
          "items": {
            "type": "integer"
          }
        },
        "zone_ids": {
          "type": "array",
          "items": {
            "type": "integer"
          }
        },
        "au_active": {
          "type": "boolean",
          "default": false
        },
        "au": {
          "type": "string",
          "description": "Optional 32 character auto-login token. Invalid values are replaced by the legacy API."
        }
      },
      "example": {
        "email": "employee@example.com",
        "username": "employee@example.com",
        "password": "secret123",
        "imeis": [
          "123456789012345"
        ],
        "modules": {
          "history": true,
          "reports": true,
          "chat": false
        },
        "active": true
      }
    },
    "SubaccountUpdate": {
      "type": "object",
      "properties": {
        "email": {
          "type": "string",
          "format": "email"
        },
        "username": {
          "type": "string"
        },
        "password": {
          "type": "string"
        },
        "imeis": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Set to an empty array to remove all object permissions."
        },
        "modules": {
          "$ref": "#/definitions/SubaccountModules"
        },
        "active": {
          "type": "boolean"
        },
        "account_expire": {
          "type": "boolean"
        },
        "account_expire_dt": {
          "type": "string",
          "format": "date"
        },
        "marker_ids": {
          "type": "array",
          "items": {
            "type": "integer"
          }
        },
        "route_ids": {
          "type": "array",
          "items": {
            "type": "integer"
          }
        },
        "zone_ids": {
          "type": "array",
          "items": {
            "type": "integer"
          }
        },
        "au_active": {
          "type": "boolean"
        },
        "au": {
          "type": "string"
        }
      },
      "example": {
        "imeis": [
          "123456789012345",
          "987654321098765"
        ],
        "modules": {
          "history": true,
          "reports": true
        },
        "active": true
      }
    },
    "SubaccountResponse": {
      "type": "object",
      "properties": {
        "success": {
          "type": "boolean",
          "example": true
        },
        "subaccount_id": {
          "type": "string"
        },
        "username": {
          "type": "string"
        },
        "email": {
          "type": "string"
        }
      }
    },
    "SubaccountUpdateResponse": {
      "allOf": [
        {
          "$ref": "#/definitions/SubaccountResponse"
        },
        {
          "type": "object",
          "properties": {
            "imeis": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      ]
    },
    "Object": {
      "type": "object",
      "properties": {
        "imei": {
          "type": "string"
        },
        "group_id": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "protocol": {
          "type": "string"
        },
        "active": {
          "type": "string"
        },
        "dt_server": {
          "type": "string"
        },
        "dt_tracker": {
          "type": "string"
        },
        "lat": {
          "type": "string"
        },
        "lng": {
          "type": "string"
        },
        "speed": {
          "type": "string"
        },
        "driver_name": {
          "type": "string"
        }
      }
    },
    "ObjectLocation": {
      "type": "object",
      "properties": {
        "imei": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "dt_server": {
          "type": "string"
        },
        "dt_tracker": {
          "type": "string"
        },
        "lat": {
          "type": "string"
        },
        "lng": {
          "type": "string"
        },
        "altitude": {
          "type": "string"
        },
        "angle": {
          "type": "string"
        },
        "speed": {
          "type": "string"
        },
        "params": {
          "type": "object"
        },
        "driver_name": {
          "type": "string"
        },
        "address": {
          "type": "string"
        },
        "loc_valid": {
          "type": "string"
        }
      }
    },
    "RouteZoneLabel": {
      "type": "object",
      "required": [
        "zone_id",
        "name"
      ],
      "description": "A current authorized account zone matching the stop or drive endpoint; not a historical zone snapshot.",
      "properties": {
        "zone_id": {
          "type": "string",
          "example": "123"
        },
        "name": {
          "type": "string",
          "example": "Apotheke Am Markt"
        }
      }
    },
    "ObjectRouteStop": {
      "type": "object",
      "additionalProperties": true,
      "description": "Existing stop fields retain their values and formats. Point indices are omitted in summary mode; zones is added only when include_zones=true.",
      "properties": {
        "id_start": {
          "type": "integer",
          "description": "First stop point index in route. Present only when summary=false."
        },
        "id_end": {
          "type": "integer",
          "description": "Last stop point index in route. Present only when summary=false."
        },
        "lat": {
          "description": "Stop latitude. Retains the legacy number/string representation; unavailable drive coordinates can be empty strings."
        },
        "lng": {
          "description": "Stop longitude. Retains the legacy number/string representation; unavailable drive coordinates can be empty strings."
        },
        "dt_start": {
          "type": "string",
          "description": "Stop start. Uses the existing account timezone."
        },
        "dt_end": {
          "type": "string",
          "description": "Stop end. Uses the existing account timezone."
        },
        "duration": {
          "type": "string",
          "description": "Existing formatted stop duration."
        },
        "address": {
          "type": "string",
          "description": "Street address, separate from zone names."
        },
        "zones": {
          "type": "array",
          "description": "Present only with include_zones=true. All current authorized matching zones; overlaps may yield multiple entries, no match yields [].",
          "items": {
            "$ref": "#/definitions/RouteZoneLabel"
          }
        }
      }
    },
    "ObjectRouteDrive": {
      "type": "object",
      "additionalProperties": true,
      "description": "Existing drive fields retain their values and formats. Point indices are omitted in summary mode; start_zones/end_zones are added only with include_zones=true.",
      "properties": {
        "id_start_s": {
          "type": "integer",
          "description": "Preceding stop point index in route. Present only when summary=false."
        },
        "id_start": {
          "type": "integer",
          "description": "Departure point index in route. Present only when summary=false."
        },
        "id_end": {
          "type": "integer",
          "description": "Arrival point index in route. Present only when summary=false."
        },
        "dt_start_s": {
          "type": "string",
          "description": "Preceding stop start. Uses the existing account timezone."
        },
        "dt_start": {
          "type": "string",
          "description": "Drive departure. Uses the existing account timezone."
        },
        "dt_end": {
          "type": "string",
          "description": "Drive arrival. Uses the existing account timezone."
        },
        "start_lat": {
          "description": "Departure latitude. Retains the legacy number/string representation; unavailable drive coordinates can be empty strings."
        },
        "start_lng": {
          "description": "Departure longitude. Retains the legacy number/string representation; unavailable drive coordinates can be empty strings."
        },
        "end_lat": {
          "description": "Arrival latitude. Retains the legacy number/string representation; unavailable drive coordinates can be empty strings."
        },
        "end_lng": {
          "description": "Arrival longitude. Retains the legacy number/string representation; unavailable drive coordinates can be empty strings."
        },
        "start_address": {
          "type": "string",
          "description": "Departure street address, separate from zone names."
        },
        "end_address": {
          "type": "string",
          "description": "Arrival street address, separate from zone names."
        },
        "duration": {
          "type": "string",
          "description": "Existing formatted drive duration."
        },
        "route_length": {
          "type": "number",
          "description": "Existing drive distance in the account distance unit."
        },
        "start_zones": {
          "type": "array",
          "description": "Present only with include_zones=true. All current authorized zones at the departure point; no match yields [].",
          "items": {
            "$ref": "#/definitions/RouteZoneLabel"
          }
        },
        "end_zones": {
          "type": "array",
          "description": "Present only with include_zones=true. All current authorized zones at the arrival point; no match yields [].",
          "items": {
            "$ref": "#/definitions/RouteZoneLabel"
          }
        }
      }
    },
    "ObjectRouteResponse": {
      "type": "object",
      "additionalProperties": true,
      "description": "Direct Legacy route response, without success/data wrapping. All properties are optional because empty routes and existing error responses may omit totals or fields. Additional existing summary, sensor, fuel and error fields retain their current shapes.",
      "properties": {
        "route": {
          "type": "array",
          "description": "Full Legacy point tuples. Omitted with summary=true.",
          "items": {
            "type": "array",
            "items": {}
          }
        },
        "events": {
          "type": "array",
          "description": "Existing route event objects. Omitted with summary=true.",
          "items": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "stops": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/ObjectRouteStop"
          }
        },
        "drives": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/ObjectRouteDrive"
          }
        },
        "route_length": {
          "type": "number",
          "description": "Existing total distance in the account distance unit."
        },
        "top_speed": {
          "description": "Existing maximum speed in account units."
        },
        "avg_speed": {
          "description": "Existing average speed in account units."
        },
        "stops_duration": {
          "type": "string",
          "description": "Existing formatted total stop duration."
        },
        "drives_duration": {
          "type": "string",
          "description": "Existing formatted total drive duration."
        },
        "engine_work": {
          "type": "string",
          "description": "Existing formatted engine work duration."
        },
        "engine_idle": {
          "type": "string",
          "description": "Existing formatted engine idle duration."
        },
        "error": {
          "type": "string",
          "description": "Existing route-processing error, when returned."
        },
        "route_data_limit": {
          "type": "integer",
          "description": "Existing raw point limit, when returned with ROUTE_DATA_LIMIT."
        }
      }
    }
  },
  "responses": {
    "Success": {
      "description": "Successful response",
      "schema": {
        "$ref": "#/definitions/SuccessEnvelope"
      }
    },
    "Error": {
      "description": "Error response",
      "schema": {
        "$ref": "#/definitions/ErrorEnvelope"
      }
    }
  },
  "tags": [
    {
      "name": "Meta"
    },
    {
      "name": "Auth"
    },
    {
      "name": "User"
    },
    {
      "name": "Support",
      "description": "Support requests from authenticated users"
    },
    {
      "name": "Subaccounts",
      "description": "Employee subaccount management for manager accounts"
    },
    {
      "name": "Objects"
    },
    {
      "name": "Reports"
    },
    {
      "name": "Travelsheet"
    },
    {
      "name": "Worktime"
    },
    {
      "name": "Tasks"
    },
    {
      "name": "Chat"
    },
    {
      "name": "Commands"
    },
    {
      "name": "History"
    },
    {
      "name": "Events"
    },
    {
      "name": "Map"
    },
    {
      "name": "Event Rules"
    },
    {
      "name": "Maintenance"
    }
  ]
}
