{ "openapi": "3.1.0", "info": { "version": "1.0.0", "title": "Navixy Platform API", "summary": "API calls for end-user applications built on the Navixy platform", "description": "This is an API reference for the Navixy Platform API, the backend used by end-user applications.\n\n**Coverage.** This specification is being adopted resource family by resource family. It currently covers the `user` family, meaning authentication, sessions, settings, personal information, audit records and applications; fleet vehicles and their service works; billing, meaning bills, payment systems, subscriptions, plans and transactions; the driver journal; depots; and field service tasks, meaning tasks, checkpoints, task history and task forms; and task routes, route optimization and recurring task schedules; and points of interest, POI import, forms and form templates; and employees, departments and check-ins; and sub-users, security groups and platform plugins; and API keys, spreadsheet imports, files, notifications, timezones and the events history; and reports, report schedules, tags and customizable entities; and geofences, geofence import, asset tracks and asset groups; and the tracking services, meaning APN lookup, BLE beacon data, delivery tracking, geocoding, map layers and data forwarding; and working statuses; and geo links, working status lists, routing and tracks.\n\n**Parameters.** All parameters are documented in the JSON request body. The API also accepts them as form-encoded body fields or query-string parameters. See the authentication page for the general rule.\n\n**Errors.** An error is signalled by a non-200 HTTP status, with an API-level code in `status.code` of the response body. The default status is 400. Validation failures add an `errors` array naming each offending parameter. Per-operation error code lists are documented on the prose pages next to each operation.\n\n**Credentials.** Most operations accept either a user session hash or an API key in the `Authorization` header. A few accept a session hash only, and reject an API key with error code 4; those are called out in the operation description.", "contact": { "name": "Navixy support", "email": "support@navixy.com", "url": "https://www.navixy.com/contact/" }, "license": { "name": "Apache 2.0", "identifier": "Apache-2.0" } }, "servers": [ { "url": "https://api.eu.navixy.com/v2", "description": "Navixy production server on European platform" }, { "url": "https://api.us.navixy.com/v2", "description": "Navixy production server on American platform" }, { "url": "https://api.me.navixy.com/v2", "description": "Navixy production server on Middle East platform" } ], "security": [ { "api_key": [] } ], "tags": [ { "name": "Users", "description": "Operations for authenticating a user, managing their sessions, reading and updating their settings and personal information, reading audit records, and registering custom applications. All resources under the /user/ path." }, { "name": "Vehicles", "description": "Vehicles in the fleet, their avatars and status list, spreadsheet import, and the service works scheduled against them. All resources under the /vehicle/ path." }, { "name": "Billing", "description": "Bills, payment systems, subscriptions, device plans and the transaction log. All resources under the /bill/, /payment_system/, /subscription/, /tariff/ and /transaction/ paths." }, { "name": "Driver journal", "description": "Trips categorised as work, personal or other, and the proposals a track can be turned into. All resources under the /driver/journal/ path." }, { "name": "Garages", "description": "Depots where vehicles are kept and maintained. All resources under the /garage/ path." }, { "name": "Tasks", "description": "Field service tasks: the places to visit, the routes and checkpoints they group into, the history of every change, and the forms field employees fill in. All resources under the /task/ path." }, { "name": "Places", "description": "Points of interest: the business-specific places employees visit, their avatars, spreadsheet import and bulk upload. All resources under the /place/ path." }, { "name": "Forms", "description": "Forms and the templates they are issued from. All resources under the /form/ path. Forms attached to tasks are under /task/form/ and carry the Tasks tag." }, { "name": "Employees", "description": "Employees and drivers, their avatars, spreadsheet import, and the departments they belong to. All resources under the /employee/ and /department/ paths." }, { "name": "Check-ins", "description": "Check-ins recorded by field employees from the Mobile Tracker App, with their photos and forms. All resources under the /checkin/ path." }, { "name": "Sub-users", "description": "Additional users under a master account: their security groups, and the trackers, places and geofences each one can see. All resources under the /subuser/ path." }, { "name": "Account and platform", "description": "Platform-level resources shared across the account, such as the plugins that change how other calls behave." }, { "name": "History", "description": "The events history: what happened on trackers and assets, what the account itself did, and which entries the user has read. All resources under the /history/ path." }, { "name": "Reports", "description": "Generating tracker reports, fetching them as JSON or as files, and scheduling them to be emailed regularly. All resources under the /report/ path." }, { "name": "Tags", "description": "Labels applied across places, tasks, employees, vehicles, geofences and trackers, and the search that finds records by them. All resources under the /tag/ path." }, { "name": "Entities", "description": "Customizable record classes and the custom fields added to them. All resources under the /entity/ path." }, { "name": "Geofences", "description": "Geofences of all three shapes, their points, and spreadsheet and KML import and export. All resources under the /zone/ path." }, { "name": "Assets", "description": "Tracks belonging to an asset rather than to a tracker, and the groups assets are organised into. All resources under the /asset/track/ and /asset_group/ paths." }, { "name": "Tracking services", "description": "Services supporting tracking rather than the trackers themselves: APN lookup, BLE beacon data, delivery tracking, geocoding, map layers and data forwarding. Resources under the /apn_settings/, /beacon/, /delivery/, /geocoder/, /map_layer/ and /retranslator/ paths." }, { "name": "Working statuses", "description": "Working statuses, the activity a tracker is marked as being in, the lists they are grouped into, and the assignment of a status to a tracker. All resources under the /status/ path." }, { "name": "Trackers", "description": "Trackers, the devices the platform tracks, together with the geo links that share their live location with people outside the account. Resources under the /tracker/ path." }, { "name": "Routes", "description": "Routing between points, using the account's default provider or a named one. All resources under the /route/ path." }, { "name": "Tracks", "description": "The trips a tracker made, the points behind them, the geofences and places they passed through, and waybills built from them. All resources under the /track/ path." } ], "paths": { "/user/auth": { "post": { "tags": [ "Users" ], "summary": "Authenticate user", "description": "Exchange a login and password for a session hash. Requires no credential of its own, and accepts no existing session.\n\nThe returned hash is what the `Authorization: NVX ` header carries on subsequent calls. An API key may be used instead of a session hash on most operations, but a small number accept a session hash only. See the operation descriptions.", "operationId": "userAuth", "security": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "login": { "type": "string", "description": "User email as login, or a demo login." }, "password": { "type": "string", "description": "User's password. 1 to 40 printable characters.", "minLength": 1, "maxLength": 40 }, "dealer_id": { "type": "integer", "description": "Optional. If specified, the API checks that the user belongs to this dealer and returns error 102 if not." } }, "required": [ "login", "password" ] } } } }, "responses": { "200": { "description": "A new session, or a challenge when a second factor is required", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "type": { "type": "string", "description": "Which outcome this is. `authenticated` means the session is ready to use. `second_factor_required` means multi-factor authentication is enabled and the returned hash is an MFA session, usable only for the `user/auth/code/*` operations until a code is verified.", "enum": [ "authenticated", "second_factor_required" ] }, "factor_type": { "type": "string", "description": "Which second factor is in use. Present only when `type` is `second_factor_required`. Only `email` is available at the moment.", "enum": [ "email" ] }, "hash": { "type": "string", "description": "Session hash to send in the `Authorization` header. When `type` is `second_factor_required` it is an MFA session and is accepted only by the `user/auth/code/*` operations." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Access denied, error code 11, when the dealer is blocked. Error codes 102 wrong login or password, 103 user not activated, 104 logins limit exceeded, and 105 login attempts limit exceeded return the default 400.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/auth/code/verify": { "post": { "tags": [ "Users" ], "summary": "Verify auth code", "description": "Complete a multi-factor authentication challenge by submitting the code that was sent to the user.\n\n**Requires an MFA session**, which is issued part-way through authentication. An ordinary session hash or API key cannot call this operation.", "operationId": "userAuthCodeVerify", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "code": { "type": "string", "description": "The verification code sent to the user." } }, "required": [ "code" ] } } } }, "responses": { "200": { "description": "Authentication completed", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "hash": { "type": "string", "description": "Session hash for the authenticated session." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/auth/code/resend": { "post": { "tags": [ "Users" ], "summary": "Resend auth code", "description": "Send the multi-factor authentication code again.\n\n**Rate limited to one call every 5 minutes** to prevent abuse.\n\n**Requires an MFA session**, which is issued part-way through authentication. An ordinary session hash or API key cannot call this operation.", "operationId": "userAuthCodeResend", "responses": { "200": { "description": "Code resent", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/activate": { "post": { "tags": [ "Users" ], "summary": "Activate user", "description": "Activate the current user account.\n\n**Requires an activation session**, so an ordinary session hash or API key cannot call this operation. Also requires the `admin` right.", "operationId": "userActivate", "responses": { "200": { "description": "Account activated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Operation not permitted, error code 13: the caller lacks the `admin` right.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/get_info": { "post": { "tags": [ "Users" ], "summary": "Get user info", "description": "Return the current user, the platform settings that apply to them, their tariff restrictions, granted rights, enabled features, and menu.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "userGetInfo", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "application": { "type": "string", "description": "Optional. If specified, the response includes a description of that application's custom menu." } } } } } }, "responses": { "200": { "description": "Current user and platform information", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "paas_id": { "type": "integer", "description": "Identifier of the platform the user belongs to." }, "paas_settings": { "$ref": "#/components/schemas/PaasSettings" }, "user_info": { "$ref": "#/components/schemas/UserInfo" }, "tariff_restrictions": { "$ref": "#/components/schemas/TariffRestrictions" }, "privileges": { "type": "object", "description": "Rights granted to the current user.", "properties": { "rights": { "type": "array", "description": "Sub-user right names held by the caller.", "items": { "type": "string" } } } }, "features": { "type": "array", "description": "Names of features enabled for the user.", "items": { "type": "string" } }, "menu": { "type": [ "object", "null" ], "description": "Deprecated menu structure. Null on current platforms." }, "user_menu": { "$ref": "#/components/schemas/UserMenu" }, "premium_gis": { "type": "boolean", "description": "Whether the premium GIS package is active." }, "hide_device_id": { "type": "boolean", "description": "Whether the web interface hides device IMEIs from this user. For a sub-user, the value of their master account." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/get_tariff_restrictions": { "post": { "tags": [ "Users" ], "summary": "Get tariff restrictions", "description": "Return the restrictions the user's tariff imposes.\n\n**Requires a weblocator session.** Also accepts GET with the same parameters as query-string values.", "operationId": "userGetTariffRestrictions", "responses": { "200": { "description": "Tariff restrictions", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/TariffRestrictions" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/resend_activation": { "post": { "tags": [ "Users" ], "summary": "Resend activation email", "description": "Send the account activation email again.\n\nRequires no credential. A session may be supplied and is used if valid, but is not required.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "userResendActivation", "security": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "login": { "type": "string", "description": "The account's login." } }, "required": [ "login" ] } } } }, "responses": { "200": { "description": "Activation email sent", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error code 265 already done, when the account is already activated and verified. Error code 264 timeout not reached, when the previous activation link was generated less than 5 minutes ago, or another server-configured timeout. The 264 body adds `timeout` and `remainder`, both ISO 8601 durations, giving the configured interval and the time left before the next attempt.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/logout": { "post": { "tags": [ "Users" ], "summary": "Log out", "description": "End the current session.\n\n**Requires a session hash. An API key is rejected** with error code 4, \"User or API key not found or session ended\", which is misleading: the key is valid, but this operation accepts standard sessions only.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "userLogout", "responses": { "200": { "description": "Session ended", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/personal_info/update": { "post": { "tags": [ "Users" ], "summary": "Update personal info", "description": "Replace the user's personal and address information.\n\n**Requires the platform plugin with id 45.** Without it the call fails with error code 222, plugin not found.\n\n**This is a full replacement, not a merge.** Fields omitted from the request are cleared.\n\n`phone` is required and must not be empty.", "operationId": "userPersonalInfoUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "legal_type": { "type": "string", "description": "Legal type of the user.", "enum": [ "legal_entity", "sole_trader", "individual" ] }, "first_name": { "type": "string", "description": "Contact person first name." }, "middle_name": { "type": "string", "description": "Contact person middle name." }, "last_name": { "type": "string", "description": "Contact person last name." }, "phone": { "type": "string", "description": "Contact phone, up to 15 digits. Required and must not be empty.", "maxLength": 15 }, "post_country": { "type": "string", "description": "Optional. Country part of the user's post address." }, "post_index": { "type": "string", "description": "Optional. Index part of the user's post address." }, "post_region": { "type": "string", "description": "Optional. Region part of the user's post address." }, "post_city": { "type": "string", "description": "Optional. City part of the user's post address." }, "post_street_address": { "type": "string", "description": "Optional. Street part of the user's post address." }, "iec": { "type": "string", "description": "Industrial Enterprises Classifier, also known as KPP. Used in Russia. For `legal_entity` only." }, "legal_name": { "type": "string", "description": "User legal name. For `legal_entity` only." }, "okpo_code": { "type": "string", "description": "Optional. All-Russian Classifier of Enterprises and Organizations, 8 or 10 characters.", "maxLength": 10 }, "state_reg_num": { "type": "string", "description": "Optional. State registration number, up to 15 characters. For example EIN in the USA or OGRN in Russia.", "maxLength": 15 }, "tin": { "type": "string", "description": "Taxpayer identification number." }, "registered_country": { "type": "string", "description": "Country part of the user's registered address." }, "registered_index": { "type": "string", "description": "Index part of the user's registered address." }, "registered_region": { "type": "string", "description": "Region part of the user's registered address." }, "registered_city": { "type": "string", "description": "City part of the user's registered address." }, "registered_street_address": { "type": "string", "description": "Street part of the user's registered address." } }, "required": [ "legal_type", "first_name", "last_name", "phone" ] } } } }, "responses": { "200": { "description": "Personal info updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/settings/read": { "post": { "tags": [ "Users" ], "summary": "Read user settings", "description": "Return the current user's settings.\n\n`file_storage_settings`, `balance_alert_settings`, `first_user_balance_warning_period`, and `second_user_balance_warning_period` are **absent entirely** unless the caller holds the `admin` right, rather than merely read-only.\n\nThe `settings` object is filtered by the dealer's GIS package before being returned, so some fields may be absent or null on platforms with a restricted package.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "userSettingsRead", "responses": { "200": { "description": "The user's settings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "settings": { "$ref": "#/components/schemas/UserSettings" }, "file_storage_settings": { "allOf": [ { "$ref": "#/components/schemas/FileStorageSettings" } ], "description": "Present only when the caller holds the `admin` right." }, "balance_alert_settings": { "allOf": [ { "$ref": "#/components/schemas/BalanceAlertSettings" } ], "description": "Present only when the caller holds the `admin` right." }, "first_user_balance_warning_period": { "type": "string", "description": "How long before a projected zero balance the first alert is sent, as a duration such as `8d`. Present only when the caller holds the `admin` right." }, "second_user_balance_warning_period": { "type": "string", "description": "How long before a projected zero balance the second alert is sent, as a duration such as `2d`. Present only when the caller holds the `admin` right." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/settings/update": { "post": { "tags": [ "Users" ], "summary": "Update user settings", "description": "Replace the current user's settings.\n\n**This is a full replacement, not a merge, and only `time_zone` is required.** Any writable field omitted from the request is cleared, including `date_format` and `hour_mode`.\n\nSend every field you intend to keep. `file_storage_settings` and `balance_alert_settings` are stored separately and do survive omission.\n\nUpdating `balance_alert_settings` or `file_storage_settings` requires the `admin` right.", "operationId": "userSettingsUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "time_zone": { "type": "string", "description": "ISO timezone ID, for example `Europe/Amsterdam`." }, "locale": { "type": "string", "description": "Locale code, for example `en` or `nl_NL`." }, "measurement_system": { "type": "string", "description": "Measurement system.", "enum": [ "metric", "imperial", "us", "metric_gal_us", "nautical" ] }, "date_format": { "type": [ "string", "null" ], "description": "Date representation. **Cleared if omitted**, and absent from the parameter list on older prose pages, which is how it gets wiped unnoticed.", "enum": [ "ddMMyyyy_dots", "ddMMyyyy_slashes", "MMddyyyy_hyphens", "yyyyMMdd_hyphens", "dMMMy", "dMMMMy" ] }, "hour_mode": { "type": [ "string", "null" ], "description": "Time representation. **Cleared if omitted**, and absent from the parameter list on older prose pages, which is how it gets wiped unnoticed.", "enum": [ "TWELVE_HOURS", "TWENTY_FOUR_HOURS" ] }, "geocoder": { "type": [ "string", "null" ], "description": "Preferred geocoder.", "enum": [ "google", "yandex", "progorod", "osm", "locationiq" ] }, "route_provider": { "type": [ "string", "null" ], "description": "Preferred route provider.", "enum": [ "google", "progorod", "osrm" ] }, "translit": { "type": "boolean", "description": "`true` if SMS notifications should be transliterated." }, "balance_alert_settings": { "allOf": [ { "$ref": "#/components/schemas/BalanceAlertSettings" } ], "description": "Requires the `admin` right." }, "file_storage_settings": { "allOf": [ { "$ref": "#/components/schemas/FileStorageSettings" } ], "description": "Requires the `admin` right." } }, "required": [ "time_zone" ] } } } }, "responses": { "200": { "description": "Settings updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Operation not permitted, error code 13: the caller lacks the `admin` right needed for `balance_alert_settings` or `file_storage_settings`.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/settings/file_storage/update": { "post": { "tags": [ "Users" ], "summary": "Update file storage settings", "description": "Replace the current user's file storage settings. Requires the `admin` right.\n\n**Send the settings fields at the top level of the request body**, not wrapped in a `file_storage_settings` object. The operation deserializes the whole body into the settings object, so a wrapped body is accepted, returns `success: true`, and silently writes defaults instead of the values supplied.\n\n**This is a full replacement**: an omitted field is written as its default.", "operationId": "userSettingsFileStorageUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "auto_overwrite": { "type": "boolean", "description": "Whether uploading a file with an existing name overwrites it. Defaults to `false` when omitted." } } } } } }, "responses": { "200": { "description": "File storage settings updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Operation not permitted, error code 13: the caller lacks the `admin` right.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/settings/ui/read": { "post": { "tags": [ "Users" ], "summary": "Read UI setting", "description": "Read one interface setting by key.\n\nA key that was never written returns `value: null` with HTTP 200. It is not an error: the operation has no error path.", "operationId": "userSettingsUiRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "key": { "type": "string", "description": "Setting key. 1 to 50 characters, and may contain only English letters, digits, `_` and `-`.", "minLength": 1, "maxLength": 50, "pattern": "^[A-Za-z0-9_-]+$" } }, "required": [ "key" ] } } } }, "responses": { "200": { "description": "The stored value", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": [ "string", "null" ], "description": "The stored value, or null if the key has never been written." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/settings/ui/update": { "post": { "tags": [ "Users" ], "summary": "Update UI setting", "description": "Write one interface setting.\n\nBoth `key` and `value` are required, and `value` may not be null. **A setting cannot be removed**, only overwritten, since no delete operation exists and null is rejected.", "operationId": "userSettingsUiUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "key": { "type": "string", "description": "Setting key. 1 to 50 characters, and may contain only English letters, digits, `_` and `-`.", "minLength": 1, "maxLength": 50, "pattern": "^[A-Za-z0-9_-]+$" }, "value": { "type": "string", "description": "Value to store. 0 to 8192 characters. May not be null, though an empty string is accepted.", "maxLength": 8192 } }, "required": [ "key", "value" ] } } } }, "responses": { "200": { "description": "Setting written", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Over quota, error code 268.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/password/change": { "post": { "tags": [ "Users" ], "summary": "Change password", "description": "Change the password for the current user.\n\n**Requires a password-change session**, so an ordinary session hash or API key cannot call this operation.\n\nChanging a password ends every other session for the account.", "operationId": "userPasswordChange", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "password": { "type": "string", "description": "New password for the user. 12 to 20 characters, combining uppercase and lowercase letters, digits, and at least one special character. A password matching any of the last 10 used is rejected.\n\nThe API enforces this through the platform password policy, which `userGetInfo` returns as `paas_settings.password_policy`, including which characters count as special.", "minLength": 12, "maxLength": 20 } }, "required": [ "password" ] } } } }, "responses": { "200": { "description": "Password changed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "In demo mode this function is disabled, error code 101.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/password/set": { "post": { "tags": [ "Users" ], "summary": "Set password", "description": "Set a new password for the current user.\n\n**Requires a session hash, and a standard session specifically. An API key is rejected** with error code 4.\n\nSetting a password ends every other session for the account.", "operationId": "userPasswordSet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "old_password": { "type": "string", "description": "Current password of the user." }, "new_password": { "type": "string", "description": "New password for the user. 12 to 20 characters, combining uppercase and lowercase letters, digits, and at least one special character. A password matching any of the last 10 used is rejected.\n\nThe API enforces this through the platform password policy, which `userGetInfo` returns as `paas_settings.password_policy`, including which characters count as special.", "minLength": 12, "maxLength": 20 } }, "required": [ "old_password", "new_password" ] } } } }, "responses": { "200": { "description": "Password set", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "In demo mode this function is disabled, error code 101. Error codes 245 new password must be different and 248 wrong password return the default 400.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/session/renew": { "post": { "tags": [ "Users" ], "summary": "Renew session", "description": "Extend the lifetime of the current session.\n\nA session expires on an absolute deadline set when it was created, **30 days later by default**, and using the session does not push that deadline back. This operation replaces the deadline with a fresh full lifetime starting now, so a session with 2 days left and one with 29 days left both end up with the same 30 days. The period is a per-installation setting, so treat 30 days as the default rather than a constant.\n\nAn API key has no expiry and needs no renewal.\n\n**Requires a session hash. An API key is rejected** with error code 4, \"User or API key not found or session ended\", which is misleading: the key is valid, but this operation accepts standard sessions only.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "userSessionRenew", "responses": { "200": { "description": "Session renewed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/session/delivery/read": { "post": { "tags": [ "Users" ], "summary": "Read delivery session", "description": "Return the delivery session hash for the current user, used by the delivery application.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "userSessionDeliveryRead", "responses": { "200": { "description": "The delivery session hash", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "string", "description": "Delivery session hash." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/session/delivery/create": { "post": { "tags": [ "Users" ], "summary": "Create delivery session", "description": "Create a delivery session for the current user.\n\nRequires the `delivery_session_create` right.\n\nTakes no parameters. Also accepts GET.", "operationId": "userSessionDeliveryCreate", "responses": { "200": { "description": "The new delivery session", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "string", "description": "Delivery session hash." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Feature unavailable due to tariff restrictions, error code 236.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "In demo mode this function is disabled, error code 101.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/session/push_token/bind": { "post": { "tags": [ "Users" ], "summary": "Bind push token", "description": "Associate a push notification token with the current session, so the platform can deliver push notifications to that device.", "operationId": "userSessionPushTokenBind", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "application": { "type": "string", "description": "Application the token belongs to.", "enum": [ "navixy_iphone_viewer", "navixy_android_viewer", "w3c_pushapi" ] }, "token": { "type": "string", "description": "Push token, or the endpoint from `pushSubscription` as a full URL when the application is `w3c_pushapi`." }, "parameters": { "type": "object", "description": "Only for `w3c_pushapi`. Carries the keys from `pushSubscription`, `p256dh` and `auth`.", "additionalProperties": true }, "category_filter": { "type": "string", "description": "Optional. Which notification categories to deliver. Defaults to `*`, meaning all. A comma-separated list allows only those categories; the same list prefixed with a minus allows all except those." } }, "required": [ "application", "token" ] } } } }, "responses": { "200": { "description": "Token bound", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/session/push_token/delete": { "post": { "tags": [ "Users" ], "summary": "Delete push token", "description": "Remove the push notification token associated with the current session.\n\nTakes no parameters. Returns error code 201, not found in database, when the session has no token bound.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "userSessionPushTokenDelete", "responses": { "200": { "description": "Token deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/audit/checkin": { "post": { "tags": [ "Users" ], "summary": "Record user check-in", "description": "Append a check-in record to the audit log for the current user. This **writes** an audit entry; it does not read one.\n\n**Requires a session hash. An API key is rejected** with error code 4, \"User or API key not found or session ended\", which is misleading: the key is valid, but this operation accepts standard sessions only.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "userAuditCheckin", "responses": { "200": { "description": "Check-in recorded", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/audit/log/list": { "post": { "tags": [ "Users" ], "summary": "List audit log records", "description": "Return audit records for the current user and their sub-users. Requires the `admin` right.\n\nNote that a request with an empty body reports `interval` as the missing required parameter. That is the internal field name; the public parameters are `from` and `to`, and both forms work.", "operationId": "userAuditLogList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "from": { "type": "string", "description": "Include records recorded at or after this date and time.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "Include records recorded at or before this date and time.", "examples": [ "2026-08-24 09:00:00" ] }, "limit": { "type": "integer", "description": "Maximum number of records to return." }, "offset": { "type": "integer", "description": "Number of records to skip." }, "subuser_ids": { "type": "array", "description": "Optional. Restrict to these sub-users.", "items": { "type": "integer" } }, "actions": { "type": "array", "description": "Optional. Restrict to these action names.", "items": { "type": "string" } }, "sort": { "type": "array", "description": "Optional. Sort options, each a property and direction such as `action_date=asc`. Sortable properties are `action`, `action_date`, `action_datetime`, `user`, and `host`. Defaults to `[\"action_date=asc\"]`.", "items": { "type": "string" } }, "grouping": { "type": "string", "description": "Optional. How to group the log.", "enum": [ "user", "action_date", "action", "default" ] } }, "required": [ "from", "to", "limit", "offset" ] } } } }, "responses": { "200": { "description": "Matching audit records", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/AuditLogRecord" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Operation not permitted, error code 13: the caller lacks the `admin` right.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/application/list": { "post": { "tags": [ "Users" ], "summary": "List user applications", "description": "List the custom applications registered for the account, enabled or not.\n\n**This operation needs no right of its own, only a valid session, and it returns the stored key of every application whose `authorization.type` is `api_key`.**\n\nCallable with an empty body. Also accepts GET.", "operationId": "userApplicationList", "responses": { "200": { "description": "The applications", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/UserApplication" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/application/create": { "post": { "tags": [ "Users" ], "summary": "Create user application", "description": "Register a custom application for the account.\n\n`id` and `enabled` are accepted and ignored: a new application is always created enabled, and `user/application/enabled/set` hides it afterwards.\n\n**`icon` is not length-checked and is stored in a 50-character column**, so a longer value fails with error code 1, a database error, rather than a validation error.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `admin` right.", "operationId": "userApplicationCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "application": { "allOf": [ { "$ref": "#/components/schemas/UserApplication" } ], "description": "The application to create. Omit `id`." } }, "required": [ "application" ] } } } }, "responses": { "200": { "description": "The created application", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "Identifier of the new application." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error code 268, over quota: the account already holds as many applications as the platform allows.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Operation not permitted, error code 13: the caller lacks the `admin` right.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/application/update": { "post": { "tags": [ "Users" ], "summary": "Update user application", "description": "Replace an existing application.\n\n**This is a full replacement, not a merge.** Every optional field left out is reset to its default: `enabled` to `true`, `authorization` to `user_session`, `display_method` to `new_tab`, `icon` to `extension`, and `description` to empty. Send the whole object, not only the fields being changed.\n\n**The length limits are not enforced here, unlike on create.** An over-long `label`, `url_template`, `description`, or `icon` fails with error code 1, a database error, instead of error code 7. Types and enum values are still checked and still give error code 7.\n\n`id` has to name an existing application of this account. Naming one that does not exist, or leaving `id` out, returns error code 201, not found in database.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `admin` right.", "operationId": "userApplicationUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "application": { "allOf": [ { "$ref": "#/components/schemas/UserApplication" } ], "description": "The replacement application, identified by its `id`." } }, "required": [ "application" ] } } } }, "responses": { "200": { "description": "Application replaced", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Operation not permitted, error code 13: the caller lacks the `admin` right.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/application/delete": { "post": { "tags": [ "Users" ], "summary": "Delete user application", "description": "Delete an application. It leaves the side menu immediately.\n\nReturns error code 201, not found in database, when no application of this account carries that identifier. **Leaving `application_id` out returns 201 as well, rather than a validation error.**\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `admin` right.", "operationId": "userApplicationDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "application_id": { "type": "integer", "description": "Identifier of the application to delete." } }, "required": [ "application_id" ] } } } }, "responses": { "200": { "description": "Application deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Operation not permitted, error code 13: the caller lacks the `admin` right.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/user/application/enabled/set": { "post": { "tags": [ "Users" ], "summary": "Show or hide a user application", "description": "Show or hide an application in the side menu, leaving everything else about it unchanged.\n\n**`enabled` is not treated as missing when it is left out: it falls back to `false` and the application is hidden.** Always send it.\n\nReturns error code 201, not found in database, when no application of this account carries that identifier. Leaving `application_id` out returns 201 as well.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `admin` right.", "operationId": "userApplicationEnabledSet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "application_id": { "type": "integer", "description": "Identifier of the application to show or hide." }, "enabled": { "type": "boolean", "description": "Whether the application appears in the side menu. Falls back to `false` when left out, which hides it." } }, "required": [ "application_id", "enabled" ] } } } }, "responses": { "200": { "description": "Visibility set", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Operation not permitted, error code 13: the caller lacks the `admin` right.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/create": { "post": { "tags": [ "Vehicles" ], "summary": "Create vehicle", "description": "Create a vehicle. Pass a vehicle object without its `id`.\n\nRequires the `vehicle_update` right.", "operationId": "vehicleCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "vehicle": { "allOf": [ { "$ref": "#/components/schemas/Vehicle" } ], "description": "The vehicle to create, without the `id` field." }, "force_reassign": { "type": "boolean", "description": "Optional. Default `true`. Reassign the device to this vehicle even if it was assigned to another one." } }, "required": [ "vehicle" ] } } } }, "responses": { "200": { "description": "The created vehicle", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new vehicle." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 247: a device cannot be assigned, or a uniqueness constraint failed.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/delete": { "post": { "tags": [ "Vehicles" ], "summary": "Delete vehicles", "description": "Delete vehicles by ID.\n\n**Exactly one of `vehicle_id` or `vehicle_ids` must be given.** A request with neither reports a missing parameter named `has_at_least_one_vehicle_id`, which is the internal name of the validation group enforcing that choice, not a parameter you can send.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `vehicle_update` right.", "operationId": "vehicleDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "vehicle_id": { "type": "integer", "description": "ID of a single vehicle to delete. Use this or `vehicle_ids`, not both." }, "vehicle_ids": { "type": "array", "description": "IDs of vehicles to delete. Use this or `vehicle_id`, not both.", "items": { "type": "integer" } } } } } } }, "responses": { "200": { "description": "Vehicles deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/list": { "post": { "tags": [ "Vehicles" ], "summary": "List vehicles", "description": "List the vehicles of the current user, with pagination, sorting and a text filter.\n\nCallable with an empty body. Also accepts GET.", "operationId": "vehicleList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "limit": { "type": "integer", "description": "Pagination. Maximum number of vehicle records to return." }, "offset": { "type": "integer", "description": "Pagination. Return vehicles starting from this offset." }, "sort": { "type": "array", "description": "Optional. Sort options, each a property and direction such as `type=desc`. **At most 2 options per request.** Sortable properties: `id`, `label`, `reg_number`, `model`, `type`, `garage_organization_name`, `vin`, `tracker_label`, `fuel_type`, `fuel_grade`, `norm_avg_fuel_consumption`, `fuel_tank_volume`, `payload_weight`, `chassis_number`, `subtype`, `wheel_arrangement`, `tyres_number`, `tyres_size`, `max_speed`, `passengers`.", "items": { "type": "string" }, "maxItems": 2 }, "filter": { "type": "string", "description": "Optional. Filter by VIN, reg_number or label. Up to 100 characters, or null.", "maxLength": 100 } } } } } }, "responses": { "200": { "description": "Matching vehicles", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Vehicle" } }, "count": { "type": "integer", "description": "Total number of vehicles matching the request, ignoring pagination." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/read": { "post": { "tags": [ "Vehicles" ], "summary": "Read vehicle", "description": "Return one vehicle by ID.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "vehicleRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "vehicle_id": { "type": "integer", "description": "ID of a vehicle." } }, "required": [ "vehicle_id" ] } } } }, "responses": { "200": { "description": "The vehicle", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Vehicle" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/update": { "post": { "tags": [ "Vehicles" ], "summary": "Update vehicle", "description": "Update a vehicle. Pass a full vehicle object including its `id`.\n\nNote `icon_id` cannot be set here: it is changed through `vehicle/avatar/assign`.\n\nRequires the `vehicle_update` right.", "operationId": "vehicleUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "vehicle": { "allOf": [ { "$ref": "#/components/schemas/Vehicle" } ], "description": "The vehicle to update, including its `id`." }, "force_reassign": { "type": "boolean", "description": "Optional. Default `true`. Reassign the device to this vehicle even if it was assigned to another one." } }, "required": [ "vehicle" ] } } } }, "responses": { "200": { "description": "Vehicle updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 247: a device cannot be assigned. Error 261: the vehicle is referenced by something that prevents the change.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/batch_convert": { "post": { "tags": [ "Vehicles" ], "summary": "Convert vehicle batch", "description": "Parse a tab-delimited batch of vehicles and return them checked, with per-row errors. Does not create anything.\n\nGive either `batch` or `file_id`. **If `file_id` is set, `batch` is ignored.** A request with neither returns error 7 with no `errors` array, because the rule is cross-field rather than per-parameter.\n\nRequires the `vehicle_update` right.", "operationId": "vehicleBatchConvert", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "batch": { "type": "string", "description": "Batch of tab-delimited vehicles." }, "file_id": { "type": "string", "description": "Preloaded file ID. Takes precedence over `batch`." }, "fields": { "type": "array", "description": "Optional. Field names, in column order. Defaults to `[\"label\", \"model\", \"reg_number\", \"fuel_grade\"]`.", "items": { "type": "string" } }, "geocoder": { "type": "string", "description": "Geocoder type." } } } } } }, "responses": { "200": { "description": "The checked batch", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/CheckedVehicle" } }, "limit_exceeded": { "type": "boolean", "description": "`true` if the given batch was constrained by a limit." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 234: the file or batch exceeds a size limit.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/avatar/assign": { "post": { "tags": [ "Vehicles" ], "summary": "Assign vehicle icon", "description": "Assign an icon to a vehicle.\n\nSet `icon_id` to null to use an uploaded avatar instead of an icon.\n\n**`icon_color` is required** as well as `vehicle_id`.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `vehicle_update` right.", "operationId": "vehicleAvatarAssign", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "vehicle_id": { "type": "integer", "description": "ID of the vehicle." }, "icon_id": { "type": [ "integer", "null" ], "description": "ID of the icon. Null means use the uploaded avatar instead." }, "icon_color": { "type": "string", "description": "Icon colour." } }, "required": [ "vehicle_id", "icon_color" ] } } } }, "responses": { "200": { "description": "Icon assigned", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/avatar/upload": { "post": { "tags": [ "Vehicles" ], "summary": "Upload vehicle avatar", "description": "Upload an avatar image for a vehicle.\n\n**This is a multipart request**, not JSON: `multipart/form-data` with the image in a part named `file`. The file MIME type must be one of `image/jpeg`, `image/pjpeg`, `image/png`, `image/gif`, or `image/webp`.\n\nThe uploaded image becomes available at `//vehicle/avatars/`, and the returned name is also what `vehicle/list` reports as `avatar_file_name`.\n\nIf `redirect_target` is passed, the response is a redirect to that URL with `response=` appended.\n\nRequires the `vehicle_update` right.", "operationId": "vehicleAvatarUpload", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "vehicle_id": { "type": "integer", "description": "Vehicle ID." }, "file": { "type": "string", "description": "The image file.", "format": "binary" }, "redirect_target": { "type": "string", "description": "Optional. URL to redirect to after upload." } }, "required": [ "vehicle_id", "file" ] } } } }, "responses": { "200": { "description": "The stored avatar", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "string", "description": "Avatar file name." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 234: the file exceeds the size limit. Error 233: the file could not be processed.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/import/start": { "post": { "tags": [ "Vehicles" ], "summary": "Start vehicle import", "description": "Start importing vehicles from a spreadsheet previously uploaded through `data/spreadsheet/parse`.\n\nRequires the `vehicle_update` right.", "operationId": "vehicleImportStart", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "filename": { "type": "string", "description": "Name of the file preloaded with `data/spreadsheet/parse`." }, "headers": { "type": "array", "description": "Column headers, naming which vehicle field each column holds.", "items": { "type": "string" } }, "user_headers": { "type": "array", "description": "Optional. User-facing labels for the headers.", "items": { "type": "string" } } }, "required": [ "filename", "headers" ] } } } }, "responses": { "200": { "description": "The import process", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "integer", "description": "Process ID, to poll with `vehicle/import/read`." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 234: the file exceeds a size limit.", "$ref": "#/components/responses/ResponseError" }, "429": { "description": "Error 15: too many requests, rate limit exceeded.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/import/read": { "post": { "tags": [ "Vehicles" ], "summary": "Read vehicle import", "description": "Return the state of one import process.", "operationId": "vehicleImportRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "process_id": { "type": "integer", "description": "ID of the import process." } }, "required": [ "process_id" ] } } } }, "responses": { "200": { "description": "The import process state", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/ImportProcess" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/import/list": { "post": { "tags": [ "Vehicles" ], "summary": "List vehicle imports", "description": "List the import processes of the current user. Callable with an empty body.", "operationId": "vehicleImportList", "responses": { "200": { "description": "Import processes", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/ImportProcess" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/import/download_failed": { "post": { "tags": [ "Vehicles" ], "summary": "Download failed import rows", "description": "Download the rows an import could not process, so they can be corrected and retried.", "operationId": "vehicleImportDownloadFailed", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "process_id": { "type": "integer", "description": "ID of the import process." } }, "required": [ "process_id" ] } } } }, "responses": { "200": { "description": "The failed rows as a file.", "content": { "text/csv": { "schema": { "type": "string" } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: entity not found.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/status/listing/read": { "post": { "tags": [ "Vehicles" ], "summary": "Read vehicle status listing", "description": "Return the vehicle status list of the current user. Callable with an empty body. Also accepts GET.", "operationId": "vehicleStatusListingRead", "responses": { "200": { "description": "The status listing", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/VehicleStatusListing" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/status/listing/update": { "post": { "tags": [ "Vehicles" ], "summary": "Update vehicle status listing", "description": "Replace the vehicle status list.\n\n`order` is ignored on update, because position is taken from the array order.\n\nRequires the `vehicle_update` right.", "operationId": "vehicleStatusListingUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "statuses": { "type": "array", "description": "The status list, in the order they should appear.", "items": { "$ref": "#/components/schemas/VehicleStatusListing" } } }, "required": [ "statuses" ] } } } }, "responses": { "200": { "description": "Status listing updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/service_task/create": { "post": { "tags": [ "Vehicles" ], "summary": "Create service task", "description": "Create a service work for one vehicle.\n\n**At least one of `conditions.mileage`, `conditions.date`, or `conditions.engine_hours` must be given.**", "operationId": "vehicleServiceTaskCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task": { "allOf": [ { "$ref": "#/components/schemas/ServiceTask" } ], "description": "The service work to create, without `id`." } }, "required": [ "task" ] } } } }, "responses": { "200": { "description": "The created service work", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new service work." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/service_task/batch/create": { "post": { "tags": [ "Vehicles" ], "summary": "Create service tasks for vehicles", "description": "Create the same service work for several vehicles at once.\n\n**At least one of `conditions.mileage`, `conditions.date`, or `conditions.engine_hours` must be given.**", "operationId": "vehicleServiceTaskBatchCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task": { "allOf": [ { "$ref": "#/components/schemas/ServiceTask" } ], "description": "The service work to create, without `id` or `vehicle_id`." }, "vehicle_ids": { "type": "array", "description": "Vehicles to create the service work for.", "items": { "type": "integer" } } }, "required": [ "task", "vehicle_ids" ] } } } }, "responses": { "200": { "description": "The created service works", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "ids": { "type": "array", "description": "IDs of the new service works.", "items": { "type": "integer" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/service_task/update": { "post": { "tags": [ "Vehicles" ], "summary": "Update service task", "description": "Update a service work. Pass a full task object including its `id`.\n\n`completion` is not editable.", "operationId": "vehicleServiceTaskUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task": { "allOf": [ { "$ref": "#/components/schemas/ServiceTask" } ], "description": "The service work to update, including its `id`." } }, "required": [ "task" ] } } } }, "responses": { "200": { "description": "Service work updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: entity not found.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/service_task/read": { "post": { "tags": [ "Vehicles" ], "summary": "Read service task", "description": "Return one service work by ID. Also accepts GET.", "operationId": "vehicleServiceTaskRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the service work." }, "return_prediction": { "type": "boolean", "description": "Optional. Default `true`. Include the legacy `prediction` field." } }, "required": [ "task_id" ] } } } }, "responses": { "200": { "description": "The service work", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/ServiceTask" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/service_task/list": { "post": { "tags": [ "Vehicles" ], "summary": "List service tasks", "description": "List service works across all of the user's vehicles. Callable with an empty body. Also accepts GET.", "operationId": "vehicleServiceTaskList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "only_unplanned": { "type": "boolean", "description": "Optional. Default `false`. If `true`, only unplanned service works are included." }, "vehicle_ids": { "type": "array", "description": "Optional. Restrict to these vehicles.", "items": { "type": "integer" } }, "statuses": { "type": "array", "description": "Optional. Restrict to these statuses.", "items": { "type": "string", "enum": [ "created", "notified", "done", "expired" ] } }, "conditions": { "type": "array", "description": "Optional. Search conditions to apply. See the search conditions reference.", "items": { "type": "object", "additionalProperties": true } }, "filter": { "type": "string", "description": "Optional. Text filter. Used together with `conditions`, both must match." }, "sort": { "type": "array", "description": "Optional. Sort options, each a property and direction such as `status=asc`. Sortable and filterable properties: `id`, `vehicle`, `vehicle_id`, `description`, `status`, `cost`, `comment`, `creation_date`, `completion_date`, `predicted_date`.", "items": { "type": "string" } }, "limit": { "type": "integer", "description": "Optional. Maximum number of service works to return." }, "offset": { "type": "integer", "description": "Optional. Offset for pagination." }, "add_filename_header": { "type": "boolean", "description": "Optional. Default `false`. If `true`, a `Content-Disposition` header is appended to the response." }, "return_prediction": { "type": "boolean", "description": "Optional. Default `true`. Include the legacy `prediction` field." } } } } } }, "responses": { "200": { "description": "Matching service works", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/ServiceTask" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/service_task/download": { "post": { "tags": [ "Vehicles" ], "summary": "Download service task report", "description": "Download a report of service works, using the same filters as the list operation, plus report-shaping parameters. Also accepts GET.", "operationId": "vehicleServiceTaskDownload", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "only_unplanned": { "type": "boolean", "description": "Optional. Default `false`. If `true`, only unplanned service works are included." }, "vehicle_ids": { "type": "array", "description": "Optional. Restrict to these vehicles.", "items": { "type": "integer" } }, "statuses": { "type": "array", "description": "Optional. Restrict to these statuses.", "items": { "type": "string", "enum": [ "created", "notified", "done", "expired" ] } }, "conditions": { "type": "array", "description": "Optional. Search conditions to apply. See the search conditions reference.", "items": { "type": "object", "additionalProperties": true } }, "filter": { "type": "string", "description": "Optional. Text filter. Used together with `conditions`, both must match." }, "sort": { "type": "array", "description": "Optional. Sort options, each a property and direction such as `status=asc`. Sortable and filterable properties: `id`, `vehicle`, `vehicle_id`, `description`, `status`, `cost`, `comment`, `creation_date`, `completion_date`, `predicted_date`.", "items": { "type": "string" } }, "limit": { "type": "integer", "description": "Optional. Maximum number of service works to return." }, "offset": { "type": "integer", "description": "Optional. Offset for pagination." }, "add_filename_header": { "type": "boolean", "description": "Optional. Default `false`. If `true`, a `Content-Disposition` header is appended to the response." }, "return_prediction": { "type": "boolean", "description": "Optional. Default `true`. Include the legacy `prediction` field." }, "format": { "type": "string", "description": "Optional. Default `pdf`. Report format.", "enum": [ "pdf", "xls", "xlsx" ] }, "group_by": { "type": "string", "description": "Optional. Group the report by vehicle or by status.", "enum": [ "vehicle", "status" ] }, "columns": { "type": "array", "description": "Optional. Report columns to include, without duplicates. When omitted the report contains every column except `creation_date`.", "items": { "type": "string", "enum": [ "vehicle_label", "creation_date", "description", "status", "mileage_remains", "hours_remains", "days_remains", "cost", "completed", "files", "comment" ] } } } } } } }, "responses": { "200": { "description": "The report as a file.", "content": { "text/csv": { "schema": { "type": "string" } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/service_task/delete": { "post": { "tags": [ "Vehicles" ], "summary": "Delete service task", "description": "Delete a service work. Also accepts GET.", "operationId": "vehicleServiceTaskDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "Optional. ID of a single service work. Give this or `task_ids`." }, "task_ids": { "type": "array", "description": "Optional. IDs of service works. Give this or `task_id`.", "items": { "type": "integer" } } } } } } }, "responses": { "200": { "description": "Service work deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/service_task/set_status": { "post": { "tags": [ "Vehicles" ], "summary": "Set service task status", "description": "Set a service work's status. On `done`, the current date and the counter values used in its conditions are saved, which freezes the wear percentage.\n\n**Only `done` is accepted at present.** Also accepts GET.", "operationId": "vehicleServiceTaskSetStatus", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the service work." }, "status": { "type": "string", "description": "The new status. Only `done` is allowed for now.", "enum": [ "done" ] } }, "required": [ "task_id", "status" ] } } } }, "responses": { "200": { "description": "Status set", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: entity not found.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/vehicle/service_task/file/create": { "post": { "tags": [ "Vehicles" ], "summary": "Create service task file", "description": "Reserve a file so its ID can be attached to a service work later. Returns an upload URL and the fields to send with it.\n\nUpload with a POST multipart request to the returned `url`, sending the values from `fields` first and the file part last, named as `file_field_name` says.", "operationId": "vehicleServiceTaskFileCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "size": { "type": "integer", "description": "Maximum size in bytes of the file to be uploaded. Reserves that much of the disk quota." }, "filename": { "type": "string", "description": "Optional. Name to store the file under. Taken from the upload form when omitted." }, "metadata": { "type": "object", "description": "Optional. Metadata object, in the same shape the task form operations use.", "additionalProperties": true }, "type": { "type": "string", "description": "Optional. Default `file`.", "enum": [ "image", "file" ] } }, "required": [ "size" ] } } } }, "responses": { "200": { "description": "Where and how to upload", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/FileUploadTarget" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/bill/create": { "post": { "tags": [ "Billing" ], "summary": "Create bill", "description": "Create a new bill for the current user, and return its ID.\n\nRequires the user bills plugin, plugin 29. Without it the call returns error 222 rather than creating anything.\n\nRequires the `payment_create` right.", "operationId": "billCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "payer": { "type": "string", "description": "Description of the payer." }, "sum": { "type": "number", "description": "Bill sum, in the default currency of the panel. From 0.01 to 999999.99.", "minimum": 0.01, "maximum": 999999.99 } }, "required": [ "payer", "sum" ] } } } }, "responses": { "200": { "description": "The created bill", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "integer", "description": "ID of the new bill." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/bill/list": { "post": { "tags": [ "Billing" ], "summary": "List bills", "description": "List the bills of the current user.\n\nRequires the user bills plugin, plugin 29, and returns error 222 without it.\n\nA bill created through `bill/create` carries exactly one entry in `positions`.\n\nOn a standalone installation the base part of each `link` can be changed with the `billing.orders.baseUrl` config option.\n\nCallable with an empty body. Also accepts GET with the same parameters as query-string values.\n\nRequires the `payment_create` right.", "operationId": "billList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "limit": { "type": "integer", "description": "Optional. Maximum number of bills to return. Both the default and the cap are 10000, and a larger value is silently reduced to it rather than rejected.", "minimum": 1 }, "offset": { "type": "integer", "description": "Optional. Default 0. Return bills starting from this offset.", "minimum": 0 } } } } } }, "responses": { "200": { "description": "Matching bills", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "count": { "type": "integer", "description": "Total number of bills, ignoring pagination." }, "bills": { "type": "array", "items": { "$ref": "#/components/schemas/Bill" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/payment_system/list": { "post": { "tags": [ "Billing" ], "summary": "List payment systems", "description": "List the payment systems available to the current user.\n\nOnly systems enabled for the user's dealer and allowed for the user's legal type are returned, so an account with none configured gets an empty list rather than an error.\n\nCallable with an empty body. Also accepts GET.\n\nRequires the `payment_create` right.", "operationId": "paymentSystemList", "responses": { "200": { "description": "Available payment systems", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentSystem" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/payment_system/estimate/get": { "post": { "tags": [ "Billing" ], "summary": "Get payment estimate", "description": "Return an estimate of the monthly payment amount for the current user.\n\nCallable with an empty body. Also accepts GET.\n\nRequires the `payment_create` right.", "operationId": "paymentSystemEstimateGet", "responses": { "200": { "description": "The estimate", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "number", "description": "Estimated payment amount, rounded up to hundreds for rubles and to tens for other currencies." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subscription/avangate/cancel": { "post": { "tags": [ "Billing" ], "summary": "Cancel subscription", "description": "Unsubscribe from 2Checkout auto-renewal, by subscription reference.\n\n2Checkout was formerly Avangate, which is where the path segment comes from.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `payment_create` right.", "operationId": "subscriptionAvangateCancel", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "reference": { "type": "string", "description": "Internal 2Checkout subscription code. Take it from the `subscription/avangate/list` response. Must not be empty.", "minLength": 1 } }, "required": [ "reference" ] } } } }, "responses": { "200": { "description": "Auto-renewal cancelled", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subscription/avangate/list": { "post": { "tags": [ "Billing" ], "summary": "List subscriptions", "description": "List the active 2Checkout subscriptions, meaning renewals, of the current user.\n\n2Checkout was formerly Avangate, which is where the path segment comes from.\n\n**Returns error 201 when 2Checkout is not configured for the caller's dealer**, which is the usual outcome on a platform that does not use it.\n\nCallable with an empty body. Also accepts GET.\n\nRequires the `payment_create` right.", "operationId": "subscriptionAvangateList", "responses": { "200": { "description": "Active subscriptions", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "type": "object", "description": "An active 2Checkout subscription.", "properties": { "reference": { "type": "string", "description": "Internal 2Checkout subscription code. Pass it to `subscription/avangate/cancel`." }, "code": { "type": "string", "description": "2Checkout product code." }, "quantity": { "type": "integer", "description": "Count." }, "expiration_date": { "type": "string", "description": "Next renewal date and time." } } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tariff/list": { "post": { "tags": [ "Billing" ], "summary": "List plans", "description": "List the device plans available to the current user.\n\nPlans come from the user's dealer when that dealer is the default dealer or a PaaS dealer, and from the parent dealer otherwise. Only plans allowed for the user's legal type are listed.\n\nCallable with an empty body. Also accepts GET.", "operationId": "tariffList", "responses": { "200": { "description": "Available plans", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Plan" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tariff/tracker/list": { "post": { "tags": [ "Billing" ], "summary": "List plans for tracker", "description": "List the plans the given tracker can be switched to, together with the wait until the next free change.\n\nThe list is returned even when the last change was less than the freeze period ago; `days_to_next_change` is what tells you whether a change is free yet.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "tariffTrackerList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account. **A blocked tracker is accepted here**, unlike most operations taking a tracker ID.", "minimum": 1 } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "Plans available for the tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Plan" } }, "days_to_next_change": { "type": "integer", "description": "Days until the next free change, or 0 when a free change is available now." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tariff/tracker/change": { "post": { "tags": [ "Billing" ], "summary": "Change tracker plan", "description": "Move a tracker from its current plan to the plan given by `tariff_id`.\n\nThe change is allowed only when all of the following hold: the tracker belongs to the user and is not a clone; the last change was more than the freeze period ago, 30 days by default; the new plan differs from the current one; both plans belong to the user's effective dealer; the new plan is active; both plans are in the same group; the new plan is for trackers; and the new plan is available to the user's legal type.\n\nWhen the last change was too recent but the current plan has an `early_change_price`, the change proceeds and that price is charged. Without one it fails with error 240.\n\n**Master users only.** A sub-user gets error 13, which the published error list does not mention.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `admin` right.", "operationId": "tariffTrackerChange", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account. **A blocked tracker is accepted here**, unlike most operations taking a tracker ID.", "minimum": 1 }, "tariff_id": { "type": "integer", "description": "ID of the new plan.", "minimum": 1 } }, "required": [ "tracker_id", "tariff_id" ] } } } }, "responses": { "200": { "description": "Plan changed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user. Error 219: the tracker is a clone. Error 221: the new plan's device limit is below the current number of devices. Error 238: the user may not switch this tracker to that plan. Error 240: the plan was last changed 30 days ago or less, and the current plan has no early change price.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 239: the new plan does not exist.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/transaction/list": { "post": { "tags": [ "Billing" ], "summary": "List transactions", "description": "List the billing transactions of the current user over a period.\n\nA request with an empty body reports `interval` as the missing parameter. That is the internal name of the field holding both dates; the public parameters are `from` and `to`.\n\nRequires the `payment_create` right.", "operationId": "transactionList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "from": { "type": "string", "description": "Start of the period. Date and time.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period. Date and time. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "limit": { "type": "integer", "description": "Optional. Maximum number of transactions to return. Both the default and the cap are 20000.", "minimum": 1, "maximum": 20000 } }, "required": [ "from", "to" ] } } } }, "responses": { "200": { "description": "Matching transactions", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Transaction" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/driver/journal/entry/list": { "post": { "tags": [ "Driver journal" ], "summary": "List driver journal entries", "description": "Return driver journal entries for one tracker.\n\nThere are two ways to select them. Give `entry_ids` to fetch entries by ID, or give `from` and `to` to fetch every entry whose own date range intersects that period.\n\nNeeds the `driver_journal` feature on the tracker's plan, and returns error 236 without it.\n\nA request with an empty body reports `interval` as the missing parameter. That is the internal name of the field holding both dates; the public parameters are `from` and `to`.", "operationId": "driverJournalEntryList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 }, "from": { "type": "string", "description": "Start of the period. Include entries whose range ends after this date.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period. Include entries whose range ends after this date. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "entry_ids": { "type": "array", "description": "Optional. Entry IDs to return. **When given, `from` and `to` are ignored** and only these entries are looked up.", "items": { "type": "integer" }, "minItems": 1 }, "types": { "type": "array", "description": "Optional. Restrict to these entry types. At most 3.", "items": { "type": "string", "enum": [ "work", "personal", "other" ] }, "maxItems": 3 }, "sort": { "type": "array", "description": "Optional. Sort options, each a column and direction such as `start_date=asc`. **At most 6 options.** Defaults to `[\"start_date=asc\"]`. Columns: `start_date` sorts by date only, ignoring the time; `start_datetime` sorts by the raw value; `end_date` and `end_datetime` are the same pair for the end; `start_address` and `end_address` sort by address; `driver` sorts by last, first and middle name rather than by employee ID; `type` sorts by entry type.", "items": { "type": "string" }, "maxItems": 6 } }, "required": [ "tracker_id", "from", "to" ] } } } }, "responses": { "200": { "description": "Matching entries", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/DriverJournalEntry" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not include the driver journal feature.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/driver/journal/entry/create": { "post": { "tags": [ "Driver journal" ], "summary": "Create driver journal entries", "description": "Create one or more driver journal entries.\n\nEach entry needs `tracker_id`, `start_date`, `end_date`, `type`, `start_location`, `end_location` and `length`. It must not carry an `id`.\n\nThree rules are checked before anything is written, and any one of them fails the whole request: every `start_date` must be before its `end_date`; the entries in the request must not overlap each other; and none may overlap an entry already stored for the same tracker. Any `employee_id` given must exist.\n\nNeeds the `driver_journal` feature on the tracker's plan, and returns error 236 without it.\n\nRequires the `vehicle_update` right.", "operationId": "driverJournalEntryCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "entries": { "type": "array", "description": "The entries to create, each without an `id`.", "items": { "$ref": "#/components/schemas/DriverJournalEntry" }, "minItems": 1 } }, "required": [ "entries" ] } } } }, "responses": { "200": { "description": "Entries created", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not include the driver journal feature.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 247: an entry overlaps another in the same request, or one already stored. The description names the offending start dates.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/driver/journal/entry/update": { "post": { "tags": [ "Driver journal" ], "summary": "Update driver journal entry", "description": "Update the type and comment of one driver journal entry.\n\n**Only `type` and `comment` may be sent, and both `id` and `type` are required.** Every other field of an entry is rejected rather than ignored: sending `tracker_id`, a date, a location, `employee_id`, `length` or an odometer value fails the request with error 7 saying that field \"must be null\". `type` is required even when only the comment is changing.\n\nNeeds the `driver_journal` feature on the tracker's plan, and returns error 236 without it.\n\nRequires the `vehicle_update` right.", "operationId": "driverJournalEntryUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "entry": { "type": "object", "description": "The change to apply.", "properties": { "id": { "type": "integer", "description": "ID of the driver journal entry to update." }, "type": { "type": "string", "description": "Type of the entry. Required, even when only `comment` changes.", "enum": [ "work", "personal", "other" ] }, "comment": { "type": [ "string", "null" ], "description": "New comment for the entry." } }, "required": [ "id", "type" ] } }, "required": [ "entry" ] } } } }, "responses": { "200": { "description": "Entry updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not include the driver journal feature.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: no entry with this `id` exists.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/driver/journal/entry/delete": { "post": { "tags": [ "Driver journal" ], "summary": "Delete driver journal entries", "description": "Delete driver journal entries by ID.\n\n**All or nothing.** If any ID in `entry_ids` does not exist, nothing is deleted and the call returns error 217.\n\nNeeds the `driver_journal` feature on the tracker's plan, and returns error 236 without it.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `vehicle_update` right.", "operationId": "driverJournalEntryDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "entry_ids": { "type": "array", "description": "IDs of the entries to delete. Must not be empty.", "items": { "type": "integer" }, "minItems": 1 } }, "required": [ "entry_ids" ] } } } }, "responses": { "200": { "description": "Entries deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not include the driver journal feature.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/driver/journal/entry/download": { "post": { "tags": [ "Driver journal" ], "summary": "Download driver journal", "description": "Return the driver journal for one tracker as a report file. Entries are selected exactly as in `driver/journal/entry/list`.\n\n**`format` is required in practice.** It carries no validation annotation, so omitting it is not reported as a missing parameter: the request fails later with error 6, \"Unexpected error\".\n\nThe response is the file itself, not a JSON envelope. With `add_filename_header` left at its default the response carries a `Content-Disposition` header naming a file such as `Driver_Journal-20260818-2210.pdf`.\n\nThe number of report downloads per user is capped by the `report.download.maxPerUser` config option.\n\nNeeds the `driver_journal` feature on the tracker's plan, and returns error 236 without it.", "operationId": "driverJournalEntryDownload", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 }, "from": { "type": "string", "description": "Start of the period. Include entries whose range ends after this date.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period. Include entries whose range ends after this date. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "entry_ids": { "type": "array", "description": "Optional. Entry IDs to return. **When given, `from` and `to` are ignored** and only these entries are looked up.", "items": { "type": "integer" }, "minItems": 1 }, "types": { "type": "array", "description": "Optional. Restrict to these entry types. At most 3.", "items": { "type": "string", "enum": [ "work", "personal", "other" ] }, "maxItems": 3 }, "sort": { "type": "array", "description": "Optional. Sort options, each a column and direction such as `start_date=asc`. **At most 6 options.** Defaults to `[\"start_date=asc\"]`. Columns: `start_date` sorts by date only, ignoring the time; `start_datetime` sorts by the raw value; `end_date` and `end_datetime` are the same pair for the end; `start_address` and `end_address` sort by address; `driver` sorts by last, first and middle name rather than by employee ID; `type` sorts by entry type.", "items": { "type": "string" }, "maxItems": 6 }, "format": { "type": "string", "description": "File format of the report.", "enum": [ "pdf", "xls", "xlsx" ] }, "group_by": { "type": "string", "description": "Optional. Put grouped entries into separate sections of the table. `type` groups by entry type, `date` groups by start date per day.", "enum": [ "type", "date" ] }, "add_filename_header": { "type": "boolean", "description": "Optional. Default `true`. Append a `Content-Disposition` header to the response." } }, "required": [ "tracker_id", "from", "to", "format" ] } } } }, "responses": { "200": { "description": "The driver journal report file", "content": { "application/pdf": { "schema": { "type": "string", "format": "binary" } }, "application/vnd.ms-excel": { "schema": { "type": "string", "format": "binary" } }, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { "schema": { "type": "string", "format": "binary" } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not include the driver journal feature.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/driver/journal/proposal/list": { "post": { "tags": [ "Driver journal" ], "summary": "List trip proposals", "description": "Return the trips over a period that could be turned into driver journal entries.\n\nA proposal is a track split at each driver change. A track with no driver change is returned whole. Tracks are selected by intersecting their date range with the requested period.\n\nUse `overlapped` to tell which proposals would collide with an entry that already exists.\n\nNeeds the `driver_journal` feature on the tracker's plan, and returns error 236 without it.\n\nA request with an empty body reports `interval` as the missing parameter. That is the internal name of the field holding both dates; the public parameters are `from` and `to`.", "operationId": "driverJournalProposalList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 }, "from": { "type": "string", "description": "Include tracks which end after this date.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "Include tracks which start before this date.", "examples": [ "2026-08-24 09:00:00" ] } }, "required": [ "tracker_id", "from", "to" ] } } } }, "responses": { "200": { "description": "Trips that could become entries", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "type": "object", "description": "A trip that could be turned into a driver journal entry.", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as `object_id`." }, "employee_id": { "type": [ "integer", "null" ], "description": "ID of the employee driving, or null when nobody was identified." }, "start_date": { "type": "string", "description": "Start date and time of the trip." }, "end_date": { "type": "string", "description": "End date and time of the trip." }, "start_location": { "allOf": [ { "$ref": "#/components/schemas/JournalLocation" } ], "description": "Where the trip starts." }, "end_location": { "allOf": [ { "$ref": "#/components/schemas/JournalLocation" } ], "description": "Where the trip ends." }, "length": { "type": "number", "description": "Length of the trip, in kilometres." }, "start_odometer": { "type": [ "number", "null" ], "description": "Odometer value at the start." }, "end_odometer": { "type": [ "number", "null" ], "description": "Odometer value at the end." }, "overlapped": { "type": "boolean", "description": "`true` when a driver journal entry already exists whose date range intersects this proposal." } } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not include the driver journal feature.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/garage/list": { "post": { "tags": [ "Garages" ], "summary": "List depots", "description": "List all depots belonging to the current user.\n\nA depot stored without a valid location omits `location` from the response entirely rather than returning it as null.\n\nCallable with an empty body. Also accepts GET.", "operationId": "garageList", "responses": { "200": { "description": "The user's depots", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Garage" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/garage/create": { "post": { "tags": [ "Garages" ], "summary": "Create depot", "description": "Create a depot, and return its ID.\n\n`mechanic_name`, `dispatcher_name` and `organization_name` are all **required**. `location` is the optional one.\n\nRequires the `vehicle_update` right.", "operationId": "garageCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "garage": { "allOf": [ { "$ref": "#/components/schemas/Garage" } ], "description": "The depot to create, without the `id` field." } }, "required": [ "garage" ] } } } }, "responses": { "200": { "description": "The created depot", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new depot." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/garage/update": { "post": { "tags": [ "Garages" ], "summary": "Update depot", "description": "Update an existing depot. Pass a full depot object including its `id`.\n\n`mechanic_name`, `dispatcher_name` and `organization_name` are all **required**. `location` is the optional one.\n\nRequires the `vehicle_update` right.", "operationId": "garageUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "garage": { "allOf": [ { "$ref": "#/components/schemas/Garage" } ], "description": "The depot to update, including its `id`." } }, "required": [ "garage" ] } } } }, "responses": { "200": { "description": "Depot updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/garage/delete": { "post": { "tags": [ "Garages" ], "summary": "Delete depot", "description": "Delete a depot by ID.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `vehicle_update` right.", "operationId": "garageDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "garage_id": { "type": "integer", "description": "ID of the depot to delete.", "minimum": 1 } }, "required": [ "garage_id" ] } } } }, "responses": { "200": { "description": "Depot deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/list": { "post": { "tags": [ "Tasks" ], "summary": "List tasks", "description": "List the tasks of the current user, with filtering, sorting and pagination.\n\nA parameter left out or set to null is simply not used as a filter.\n\nCallable with an empty body. Also accepts GET with the same parameters as query-string values.", "operationId": "taskList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "external_id": { "type": "string", "description": "Optional. External task ID to search for. Up to 100 characters.", "maxLength": 100 }, "statuses": { "type": "array", "description": "Optional. Default all. Restrict to these task statuses.", "items": { "type": "string", "enum": [ "unassigned", "assigned", "done", "failed", "delayed", "arrived", "faulty" ] }, "minItems": 1 }, "trackers": { "type": "array", "description": "Optional. IDs of the trackers the tasks are assigned to. Must not contain duplicates.", "items": { "type": "integer" } }, "from": { "type": "string", "description": "Optional. Include tasks that are actual after this date.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "Optional. Include tasks that are actual before this date.", "examples": [ "2026-08-24 09:00:00" ] }, "conditions": { "type": "array", "description": "Optional. Search conditions to apply. **At most 60.** Conditions may be built on `id`, `employee` (an ID), `status`, `label`, `location` (the address), `from`, `to`, `status_change_date`, `arrival_date`, `stay_duration` (seconds), `description`, `external_id`, and `form` (the template ID).", "items": { "type": "object", "additionalProperties": true }, "maxItems": 60 }, "filter": { "type": "string", "description": "Optional. Filter over all built-in and custom fields, up to 100 characters. Used together with `conditions`: both must match for a task to be returned.", "maxLength": 100 }, "filters": { "type": "array", "description": "Optional. Filters for task label, description or address.", "items": { "type": "string" } }, "tag_ids": { "type": "array", "description": "Optional. Restrict to tasks carrying these tag IDs.", "items": { "type": "integer" } }, "location": { "type": "object", "description": "Optional. Return only tasks whose zone centre lies inside this circle.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "radius": { "type": "integer", "description": "Radius in metres." } } }, "sort": { "type": "array", "description": "Optional. Sort options, each a property and direction such as `status=asc`. Sortable properties: `id`, `employee` (full name, or the tracker label when there is no employee), `status`, `label`, `location` (the address), `from`, `to`, `status_change_date`, `arrival_date`, `stay_duration`, `description`, `external_id`, and `form` (the form label).", "items": { "type": "string" } }, "offset": { "type": "integer", "description": "Optional. Default 0. Offset into the found tasks, for pagination.", "minimum": 0 }, "limit": { "type": "integer", "description": "Optional. Default 10000, maximum 100000. Number of tasks to return.", "minimum": 0, "maximum": 100000 } } } } } }, "responses": { "200": { "description": "Matching tasks", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Task" } }, "count": { "type": "integer", "description": "Total number of tasks found, ignoring pagination." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/read": { "post": { "tags": [ "Tasks" ], "summary": "Read task", "description": "Return a task, a checkpoint, or a route with its checkpoints, by ID.\n\nWhich of the three you get depends on what the ID belongs to. `checkpoints` is present only when the ID is a route.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "taskRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the task, route or checkpoint.", "minimum": 1 } }, "required": [ "task_id" ] } } } }, "responses": { "200": { "description": "The task, checkpoint or route", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Task" }, "checkpoints": { "type": "array", "description": "Every checkpoint of the route. Present only when the given ID is a route.", "items": { "$ref": "#/components/schemas/TaskCheckpoint" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/create": { "post": { "tags": [ "Tasks" ], "summary": "Create task", "description": "Create a task, and return its ID.\n\n`to` is required and **cannot be in the past**, and `from` cannot be after `to`. Both rules are checked before anything else and report `task.to` or `task.from` rather than a top-level parameter.\n\nSetting `tracker_id` assigns the task to the employee associated with that tracker and starts it in `assigned` state. Leaving it out starts the task `unassigned`.\n\nEach call creates a new task even when the parameters are identical, because the ID is generated by the server. If a task has to stay tied to a record in an external system, keep the returned ID, or use `external_id`.\n\nRequires the `task_update` right.", "operationId": "taskCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task": { "allOf": [ { "$ref": "#/components/schemas/Task" } ], "description": "The task to create. Read-only fields are ignored, and `id` is always ignored here." }, "create_form": { "type": "boolean", "description": "Optional. Default `false`, for backward compatibility. When `true`, the `form_template_id` inside `task` is read and a form is created from that template." } }, "required": [ "task" ] } } } }, "responses": { "200": { "description": "The created task", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new task." }, "external_id_counts": { "$ref": "#/components/schemas/ExternalIdCounts" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not allow tasks.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/update": { "post": { "tags": [ "Tasks" ], "summary": "Update task", "description": "Update a task. Pass a full task object including its `id`.\n\n**`tracker_id` is ignored**, so this call cannot reassign a task. Use `task/assign` for that. The task owner cannot be changed either.\n\n`to` is required and **cannot be in the past**, and `from` cannot be after `to`. Both rules are checked before anything else and report `task.to` or `task.from` rather than a top-level parameter. `task.id` must not be null.\n\nOnly a task in `unassigned` or `assigned` state can be changed. Anything else returns error 255.\n\nRequires the `task_update` right.", "operationId": "taskUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task": { "allOf": [ { "$ref": "#/components/schemas/Task" } ], "description": "The task to update, including its `id`. Read-only fields and `tracker_id` are ignored." }, "create_form": { "type": "boolean", "description": "Optional. Default `false`, for backward compatibility. When `true`, the `form_template_id` inside `task` is read and the task's form is created, replaced or deleted to match it." } }, "required": [ "task" ] } } } }, "responses": { "200": { "description": "Task updated", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "external_id_counts": { "$ref": "#/components/schemas/ExternalIdCounts" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/assign": { "post": { "tags": [ "Tasks" ], "summary": "Assign task", "description": "Assign a task to a tracker, reassign it to a different one, or leave it unassigned.\n\nPass `tracker_id` as null, or leave it out, to unassign.\n\nOnly a task in `unassigned` or `assigned` state can be changed. Anything else returns error 255.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskAssign", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the task to assign.", "minimum": 1 }, "tracker_id": { "type": [ "integer", "null" ], "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked. Null leaves the task assigned to no one.", "minimum": 1 } }, "required": [ "task_id" ] } } } }, "responses": { "200": { "description": "Task assigned", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not allow tasks.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: no tracker with this ID belongs to the authorized user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/delete": { "post": { "tags": [ "Tasks" ], "summary": "Delete task", "description": "Delete a task by ID.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the task to delete.", "minimum": 1 } }, "required": [ "task_id" ] } } } }, "responses": { "200": { "description": "Task deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/count": { "post": { "tags": [ "Tasks" ], "summary": "Count tasks", "description": "Return the total number of tasks belonging to the current user.\n\nCallable with an empty body. Also accepts GET.", "operationId": "taskCount", "responses": { "200": { "description": "The count", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "count": { "type": "integer", "description": "Number of tasks." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/transmute": { "post": { "tags": [ "Tasks" ], "summary": "Convert task to checkpoint", "description": "Convert a task into a checkpoint of an existing route.\n\nThe checkpoint is inserted at `order`, and any checkpoint already at that position, along with everything after it, shifts one place right.\n\nOnly a task in `unassigned` or `assigned` state can be changed. Anything else returns error 255. The same applies to every checkpoint already on the target route.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskTransmute", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the task to convert.", "minimum": 1 }, "route_id": { "type": "integer", "description": "ID of the route to attach it to.", "minimum": 1 }, "order": { "type": "integer", "description": "Zero-based position at which to insert the checkpoint into the route.", "minimum": 0 } }, "required": [ "task_id", "route_id", "order" ] } } } }, "responses": { "200": { "description": "Task converted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/batch_convert": { "post": { "tags": [ "Tasks" ], "summary": "Convert task batch", "description": "Parse a tab-delimited batch of tasks and return them checked, with per-row errors. **Creates nothing.**\n\nGive either `batch` or `file_id`. A request with neither returns error 7 with the message \"The 'batch' or 'file_id' parameter must be specified\".\n\nRecognised column names are `label`, `from`, `to`, `address`, `lat`, `lng`, `radius`, `description`, `external_id`, `performer`, `max_delay`, `min_stay_duration` and `min_arrival_duration`. Any other column name is read as a custom field ID and ignored if no such field exists.\n\nWhen only `from` is given for a row, `to` is set to `from` plus `default_duration`.\n\nRequires the `task_update` right.", "operationId": "taskBatchConvert", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "batch": { "type": "string", "description": "Batch of tab-delimited tasks." }, "file_id": { "type": "string", "description": "Preloaded file ID, as an alternative to `batch`." }, "fields": { "type": "array", "description": "Optional. Column names, in order. Defaults to `[\"label\", \"from\", \"to\", \"address\", \"lat\", \"lng\", \"description\", \"min_stay_duration\", \"max_delay\", \"min_arrival_duration\"]`.", "items": { "type": "string" } }, "geocoder": { "type": "string", "description": "Geocoder type to use for resolving addresses." }, "default_radius": { "type": "integer", "description": "Optional. Default 100. Zone radius in metres for a row that gives no `radius`." }, "default_max_delay": { "type": "integer", "description": "Optional. Default 0. Maximum delay in minutes for a row that gives no `max_delay`." }, "default_duration": { "type": "integer", "description": "Optional. Default 60. Task duration in minutes, used to derive `to` from `from`." }, "default_min_stay_duration": { "type": "integer", "description": "Optional. Default 0. Minimum stay in minutes for a row that gives no `min_stay_duration`." }, "location_check_mode": { "type": "string", "description": "Optional. Default `entity_location`. How to match a row to a place. With `entity_location`, `vehicle_ids` is ignored.", "enum": [ "no_check", "entity_location", "parent_location" ] }, "employee_ids": { "type": "array", "description": "Optional. Employees to distribute the tasks between automatically.", "items": { "type": "integer" } }, "vehicle_ids": { "type": "array", "description": "Optional. Vehicles to distribute the tasks between automatically. **Ignored** when `location_check_mode` is `entity_location`.", "items": { "type": "integer" } } } } } } }, "responses": { "200": { "description": "The checked batch", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "description": "The parsed tasks, each with any problems found while checking it.", "items": { "$ref": "#/components/schemas/CheckedTask" } }, "limit_exceeded": { "type": "boolean", "description": "`true` if the given batch was constrained by a limit." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/history/list": { "post": { "tags": [ "Tasks" ], "summary": "List task history", "description": "Return the recorded history of one task: its creation, updates, assignments and status changes.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "taskHistoryList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the task.", "minimum": 1 } }, "required": [ "task_id" ] } } } }, "responses": { "200": { "description": "The task history", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/TaskHistoryEntry" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/checkpoint/list": { "post": { "tags": [ "Tasks" ], "summary": "List checkpoints", "description": "Return checkpoints by ID.\n\n**`checkpoint_ids` is required and must not be empty**, so this operation reads a known set rather than browsing all checkpoints.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "taskCheckpointList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "checkpoint_ids": { "type": "array", "description": "IDs of the checkpoints to return. Must not be empty.", "items": { "type": "integer" }, "minItems": 1 } }, "required": [ "checkpoint_ids" ] } } } }, "responses": { "200": { "description": "Matching checkpoints", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/TaskCheckpoint" } }, "count": { "type": "integer", "description": "Total number of checkpoints found." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/checkpoint/read": { "post": { "tags": [ "Tasks" ], "summary": "Read checkpoint", "description": "Return one checkpoint by ID.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "taskCheckpointRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "checkpoint_id": { "type": "integer", "description": "ID of the checkpoint.", "minimum": 1 } }, "required": [ "checkpoint_id" ] } } } }, "responses": { "200": { "description": "The checkpoint", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/TaskCheckpoint" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/checkpoint/create": { "post": { "tags": [ "Tasks" ], "summary": "Create checkpoint", "description": "Create a checkpoint on an existing route, and return its ID.\n\nThe checkpoint is inserted at `order` in the parent route's checkpoint list, and any checkpoint already at that position, along with everything after it, shifts one place right.\n\n`to` is required and **cannot be in the past**, and `from` cannot be after `to`. Both rules are checked before anything else and report `task.to` or `task.from` rather than a top-level parameter.\n\nRequires the `task_update` right.", "operationId": "taskCheckpointCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "checkpoint": { "allOf": [ { "$ref": "#/components/schemas/TaskCheckpoint" } ], "description": "The checkpoint to create, without `id`. Read-only fields are ignored." }, "create_form": { "type": "boolean", "description": "Optional. Default `false`. When `true`, the `form_template_id` inside `checkpoint` is read and a form is created from that template." } }, "required": [ "checkpoint" ] } } } }, "responses": { "200": { "description": "The created checkpoint", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new checkpoint." }, "external_id_counts": { "$ref": "#/components/schemas/ExternalIdCounts" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not allow tasks.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/checkpoint/update": { "post": { "tags": [ "Tasks" ], "summary": "Update checkpoint", "description": "Update a checkpoint. Pass a full checkpoint object including its `id`.\n\n**`tracker_id` is ignored**: a checkpoint inherits its performer from its route.\n\n`to` is required and **cannot be in the past**, and `from` cannot be after `to`. Both rules are checked before anything else and report `task.to` or `task.from` rather than a top-level parameter.\n\nOnly a task in `unassigned` or `assigned` state can be changed. Anything else returns error 255.\n\nRequires the `task_update` right.", "operationId": "taskCheckpointUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "checkpoint": { "allOf": [ { "$ref": "#/components/schemas/TaskCheckpoint" } ], "description": "The checkpoint to update, including its `id`. Read-only fields and `tracker_id` are ignored." }, "create_form": { "type": "boolean", "description": "Optional. Default `false`. When `true`, the `form_template_id` inside `checkpoint` is read and the form is created, replaced or deleted to match it." } }, "required": [ "checkpoint" ] } } } }, "responses": { "200": { "description": "Checkpoint updated", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "external_id_counts": { "$ref": "#/components/schemas/ExternalIdCounts" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/checkpoint/delete": { "post": { "tags": [ "Tasks" ], "summary": "Delete checkpoint", "description": "Delete a checkpoint by ID. The remaining checkpoints on the route close the gap.\n\nOnly a task in `unassigned` or `assigned` state can be changed. Anything else returns error 255.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskCheckpointDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "checkpoint_id": { "type": "integer", "description": "ID of the checkpoint to delete.", "minimum": 1 } }, "required": [ "checkpoint_id" ] } } } }, "responses": { "200": { "description": "Checkpoint deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/checkpoint/transmute": { "post": { "tags": [ "Tasks" ], "summary": "Convert checkpoint to task", "description": "Convert a checkpoint back into a standalone task, detaching it from its route.\n\nOnly a task in `unassigned` or `assigned` state can be changed. Anything else returns error 255. Every checkpoint on the route is checked, not just this one.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskCheckpointTransmute", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "checkpoint_id": { "type": "integer", "description": "ID of the checkpoint to convert.", "minimum": 1 } }, "required": [ "checkpoint_id" ] } } } }, "responses": { "200": { "description": "Checkpoint converted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/form/create": { "post": { "tags": [ "Tasks" ], "summary": "Attach form to task", "description": "Attach a new form to an existing task or checkpoint. A form is always created from a form template.\n\nAllowed while the task is `unassigned`, `assigned` or `arrived`. A sub-user with the `completed_form_update` right may also act on a `done`, `failed` or `delayed` task. Anything else returns error 255.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskFormCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the task or checkpoint to attach the form to.", "minimum": 1 }, "template_id": { "type": "integer", "description": "ID of the form template to build the form from.", "minimum": 1 } }, "required": [ "task_id", "template_id" ] } } } }, "responses": { "200": { "description": "Form attached", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 247: the task already has a form attached.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/form/delete": { "post": { "tags": [ "Tasks" ], "summary": "Delete task form", "description": "Detach a form from its task. **All data entered in the form is lost.**\n\nAllowed while the task is `unassigned`, `assigned` or `arrived`. Anything else returns error 255.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskFormDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the task the form is attached to.", "minimum": 1 } }, "required": [ "task_id" ] } } } }, "responses": { "200": { "description": "Form deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/form/read": { "post": { "tags": [ "Tasks" ], "summary": "Read task form", "description": "Return the form attached to a task, together with every file associated with its fields, submitted or not.\n\n`value` is null when the task has no form.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "taskFormRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the task.", "minimum": 1 } }, "required": [ "task_id" ] } } } }, "responses": { "200": { "description": "The form and its files", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "oneOf": [ { "$ref": "#/components/schemas/Form" }, { "type": "null" } ], "description": "The form, or null when no form is attached." }, "files": { "type": "array", "description": "Files associated with this form's fields, both submitted and unsubmitted.", "items": { "$ref": "#/components/schemas/FormFile" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/form/download": { "post": { "tags": [ "Tasks" ], "summary": "Download task form", "description": "Return the form attached to a task rendered as a file.\n\nThe response is the file itself, not a JSON envelope.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "taskFormDownload", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the task.", "minimum": 1 }, "format": { "type": "string", "description": "Format of the downloaded file.", "enum": [ "xlsx", "pdf" ] } }, "required": [ "task_id", "format" ] } } } }, "responses": { "200": { "description": "The rendered form file", "content": { "application/pdf": { "schema": { "type": "string", "format": "binary" } }, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { "schema": { "type": "string", "format": "binary" } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/form/list": { "post": { "tags": [ "Tasks" ], "summary": "List forms", "description": "List forms created from a given template, or belonging to given tasks, with the related tracker, vehicle, employee and task for each.\n\nThis is the one form operation that is not restricted to task forms.\n\n**`include_unsubmitted` is the only required parameter.** `ascending` is not required, and a request carrying only `include_unsubmitted` succeeds.\n\nGiving neither `template_id` nor `task_ids` is not rejected, but the list comes back empty, so one of them is needed in practice.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "taskFormList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "include_unsubmitted": { "type": "boolean", "description": "Whether to include forms that have not been submitted yet." }, "template_id": { "type": "integer", "description": "Optional. Return forms built from this template.", "minimum": 1 }, "task_ids": { "type": "array", "description": "Optional. Return forms belonging to these tasks. Between 1 and 5000 IDs.", "items": { "type": "integer" }, "minItems": 1, "maxItems": 5000 }, "order_by": { "type": "string", "description": "Optional. Default `submitted`. Field to sort the list by.", "enum": [ "task_id", "created", "submitted", "task_address", "submit_address", "employee_full_name", "vehicle_label", "tracker_label", "task_label", "task_creation_date", "task_from", "task_to", "task_arrival_date", "task_completion_date", "form_label", "form_description" ] }, "ascending": { "type": "boolean", "description": "Optional. Default `true`. Sort direction." }, "filters": { "type": "object", "description": "Optional. Narrow the list by field value. Conditions combine with logical AND. Every value except `task_id` is a partial match; `task_id` is exact.", "properties": { "employee_full_name": { "type": "string", "description": "Partial match against the associated employee's name." }, "form_description": { "type": "string", "description": "Partial match against the form description." }, "form_label": { "type": "string", "description": "Partial match against the form label." }, "submit_address": { "type": "string", "description": "Partial match against the address the form was submitted at." }, "task_id": { "type": "integer", "description": "Exact match against the task ID." }, "task_address": { "type": "string", "description": "Partial match against the task address." }, "task_label": { "type": "string", "description": "Partial match against the task label." }, "tracker_label": { "type": "string", "description": "Partial match against the tracker label." }, "vehicle_label": { "type": "string", "description": "Partial match against the vehicle label." } } }, "submit_period": { "$ref": "#/components/schemas/Period" }, "task_creation_period": { "$ref": "#/components/schemas/Period" }, "task_from_period": { "$ref": "#/components/schemas/Period" }, "task_to_period": { "$ref": "#/components/schemas/Period" }, "task_arrival_period": { "$ref": "#/components/schemas/Period" }, "task_completion_period": { "$ref": "#/components/schemas/Period" }, "offset": { "type": "integer", "description": "Optional. Default 0. Offset into the results, for pagination.", "minimum": 0 }, "limit": { "type": "integer", "description": "Optional. Default 10000, maximum 100000. Number of forms to return.", "minimum": 0, "maximum": 100000 } }, "required": [ "include_unsubmitted" ] } } } }, "responses": { "200": { "description": "Matching forms", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "count": { "type": "integer", "description": "Total number of forms matching the query." }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/FormListEntry" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: no form template with this ID belongs to the authorized user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/form/values/update": { "post": { "tags": [ "Tasks" ], "summary": "Update form values", "description": "Correct the values of a form already attached to a task.\n\n**This is for correcting a submitted form, not for filling an empty one.** The form must have been submitted at least once from the Mobile Tracker App, and the task's device must not be deleted.\n\nAllowed while the task is `unassigned`, `assigned` or `arrived`. A sub-user with the `completed_form_update` right may also act on a `done`, `failed` or `delayed` task. Anything else returns error 255.\n\nWhen a value fails validation the call returns error 242 and an `errors` array whose entries carry `field_id`, a numeric `code`, and a message. The codes are listed on this page.\n\nRequires the `task_update` right.", "operationId": "taskFormValuesUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the task.", "minimum": 1 }, "values": { "type": "object", "description": "Map of form field ID, as a string, to a value object. A value object carries a `type` matching the field type and a `value`. See the form fields and values reference.", "additionalProperties": true } }, "required": [ "task_id", "values" ] } } } }, "responses": { "200": { "description": "Form values updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 101: the current user has the demo flag, and this operation is disabled in demo mode.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/form/file/create": { "post": { "tags": [ "Tasks" ], "summary": "Create form file", "description": "Reserve a file against a form field and return the credentials for uploading it.\n\nThis does not upload anything. It reserves space in the account's file quota and returns where and how to POST the file. Upload it as multipart form data to the returned `url`, sending the values from `fields` first and the file part last, named as `file_field_name` says.\n\nA reserved file that is never uploaded is deleted after `expires`. A file that is uploaded but never submitted as a form field value is deleted at the next form submission, so creating and uploading is not enough: the form must then be submitted with the file ID as the field's value.\n\nAllowed while the task is `unassigned`, `assigned` or `arrived`. A sub-user with the `completed_form_update` right may also act on a `done`, `failed` or `delayed` task. Anything else returns error 255. The form must also have been submitted at least once.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskFormFileCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "task_id": { "type": "integer", "description": "ID of the task the form is attached to.", "minimum": 1 }, "field_id": { "type": "string", "description": "ID of the form field the file attaches to. The field must be file-based." }, "size": { "type": "integer", "description": "Maximum size in bytes of the file to be uploaded, used to reserve quota. The platform default cap is 16 MB.", "minimum": 1 }, "filename": { "type": "string", "description": "Optional. Name to give the uploaded file. Without it the name comes from the upload itself." }, "metadata": { "type": "object", "description": "Optional. Metadata for the file, for images only.", "additionalProperties": true } }, "required": [ "task_id", "field_id", "size" ] } } } }, "responses": { "200": { "description": "Where to upload the file", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/FileUploadTarget" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 268: the file cannot be created without violating the account's file quota. Error 271: `size` is above the maximum allowed, 16 MB by default.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 267: six or more unsubmitted files are already associated with this form field.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/route/list": { "post": { "tags": [ "Tasks" ], "summary": "List routes", "description": "List the routes of the current user, with optional filtering.\n\nA parameter left out or set to null is simply not used as a filter.\n\nCallable with an empty body. Also accepts GET with the same parameters as query-string values.", "operationId": "taskRouteList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "statuses": { "type": "array", "description": "Optional. Default all. Restrict to these route statuses.", "items": { "type": "string", "enum": [ "unassigned", "assigned", "done", "failed", "delayed", "arrived", "faulty" ] }, "minItems": 1 }, "trackers": { "type": "array", "description": "Optional. IDs of the trackers the routes are assigned to.", "items": { "type": "integer" } }, "from": { "type": "string", "description": "Optional. Include routes that are actual after this date.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "Optional. Include routes that are actual before this date.", "examples": [ "2026-08-24 09:00:00" ] }, "filter": { "type": "string", "description": "Optional. Filter over route label and description, up to 100 characters.", "maxLength": 100 } } } } } }, "responses": { "200": { "description": "Matching routes", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/TaskRoute" } }, "count": { "type": "integer", "description": "Total number of routes found." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/route/read": { "post": { "tags": [ "Tasks" ], "summary": "Read route", "description": "Return one route by ID.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "taskRouteRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "route_id": { "type": "integer", "description": "ID of the route.", "minimum": 1 } }, "required": [ "route_id" ] } } } }, "responses": { "200": { "description": "The route", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/TaskRoute" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/route/create": { "post": { "tags": [ "Tasks" ], "summary": "Create route", "description": "Create a route together with its checkpoints, and return the created route.\n\n**A route needs between 2 and 500 checkpoints.** Sending one returns error 7 with \"size must be between 2 and 500\".\n\nGive the checkpoints in order of execution. Their windows must agree with each other: a checkpoint's `to` cannot be before the `from` of a preceding one. The route's own `from` and `to` are computed from the first and last checkpoint, which is why both are ignored on the route object.\n\nOne checkpoint may carry an `id`, in which case it must be an existing **task**, and it is converted into a checkpoint of this route.\n\n`external_id_counts` reports each external ID that now has a count above zero. Different checkpoints may carry different external IDs, so several can come back. When there is nothing to report the field is absent.\n\nRequires the `task_update` right.", "operationId": "taskRouteCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "route": { "allOf": [ { "$ref": "#/components/schemas/TaskRoute" } ], "description": "The route to create. Read-only fields are ignored, as are `from`, `to` and `checkpoint_ids`." }, "checkpoints": { "type": "array", "description": "The checkpoints, in order of execution. Between 2 and 500.", "items": { "$ref": "#/components/schemas/TaskCheckpoint" }, "minItems": 2, "maxItems": 500 }, "create_form": { "type": "boolean", "description": "Optional. Default `false`, for backward compatibility. When `true`, the `form_template_id` inside each checkpoint is read and a form is created from that template." } }, "required": [ "route", "checkpoints" ] } } } }, "responses": { "200": { "description": "The created route", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "result": { "$ref": "#/components/schemas/TaskRoute" }, "external_id_counts": { "$ref": "#/components/schemas/ExternalIdCounts" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not allow tasks.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/route/update": { "post": { "tags": [ "Tasks" ], "summary": "Update route", "description": "Update a route and its checkpoints, and return the updated route.\n\nReordering the IDs in `checkpoint_ids` changes the order of execution. The route owner cannot be changed.\n\n**Checkpoints not included in the request are deleted.** A checkpoint carrying an `id` is updated, one without an `id` is created. `checkpoints` should be null when the route's `checkpoint_ids` is null, and not null otherwise.\n\nOnly a route in `unassigned` or `assigned` state can be changed. Anything else returns error 255.\n\nRequires the `task_update` right.", "operationId": "taskRouteUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "route": { "allOf": [ { "$ref": "#/components/schemas/TaskRoute" } ], "description": "The route to update, including its `id`. Read-only fields and `tracker_id` are ignored." }, "checkpoints": { "type": "array", "description": "The checkpoints this route should end up with, in order. Anything absent is deleted.", "items": { "$ref": "#/components/schemas/TaskCheckpoint" } }, "create_form": { "type": "boolean", "description": "Optional. Default `false`, for backward compatibility. When `true`, the `form_template_id` inside each checkpoint is read and that checkpoint's form is created, replaced or deleted to match it." } }, "required": [ "route" ] } } } }, "responses": { "200": { "description": "The updated route", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "result": { "$ref": "#/components/schemas/TaskRoute" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/route/assign": { "post": { "tags": [ "Tasks" ], "summary": "Assign route", "description": "Assign a route to a tracker, reassign it to a different one, or leave it unassigned.\n\nPass `tracker_id` as null, or leave it out, to unassign.\n\nOnly a route in `unassigned` or `assigned` state can be changed. Anything else returns error 255.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskRouteAssign", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "route_id": { "type": "integer", "description": "ID of the route to assign.", "minimum": 1 }, "tracker_id": { "type": [ "integer", "null" ], "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked. Null leaves the route assigned to no one.", "minimum": 1 } }, "required": [ "route_id" ] } } } }, "responses": { "200": { "description": "Route assigned", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not allow tasks.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: no tracker with this ID belongs to the authorized user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/route/delete": { "post": { "tags": [ "Tasks" ], "summary": "Delete route", "description": "Delete a route **and all of its checkpoints** by ID.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskRouteDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "route_id": { "type": "integer", "description": "ID of the route to delete.", "minimum": 1 } }, "required": [ "route_id" ] } } } }, "responses": { "200": { "description": "Route deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/route/points/optimize": { "post": { "tags": [ "Tasks" ], "summary": "Optimize route points", "description": "Return the order in which a set of points should be visited to minimise travel time. **Creates nothing**, so neither a route nor its checkpoints need to exist first.\n\nThe suggested order respects each point's time window. Points with earlier windows get lower positions, and where windows overlap the order may vary to make the route as a whole more efficient.\n\n**Limits: 2 to 49 points to visit, plus the start point, and 5000 km total distance.** Sending one point returns error 7 with \"The number of points must be in the range [2..49]\".\n\n`result` is the new order expressed as source indexes: the first entry says which of the points you sent should be visited first. So `[2, 0, 1]` means visit the third point you sent, then the first, then the second.\n\nRequires the `task_update` right.", "operationId": "taskRoutePointsOptimize", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "start_point": { "type": "object", "description": "Where the performer departs from.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "departure": { "type": "string", "description": "Optional. When the performer departs." } } }, "route_points": { "type": "array", "description": "The points to visit, between 2 and 49 of them.", "items": { "type": "object", "properties": { "location": { "type": "object", "description": "Where the point is.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } }, "from": { "type": "string", "description": "Start of the window in which this point can be visited.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the window in which this point can be visited.", "examples": [ "2026-08-24 09:00:00" ] } } }, "minItems": 2, "maxItems": 49 } }, "required": [ "start_point", "route_points" ] } } } }, "responses": { "200": { "description": "The suggested order", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "result": { "type": "array", "description": "Indexes into `route_points`, in the order the points should be visited.", "items": { "type": "integer" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 210: the overall route distance is more than the 5000 km limit. Error 264: timeout not reached, meaning the call rate is too high.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/schedule/list": { "post": { "tags": [ "Tasks" ], "summary": "List task schedules", "description": "List the task or route schedules of the current user, including every unassigned one.\n\nCallable with an empty body. Also accepts GET with the same parameters as query-string values.", "operationId": "taskScheduleList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Optional. IDs of the trackers the schedules are assigned to.", "items": { "type": "integer" } }, "filter": { "type": "string", "description": "Optional. Filter over schedule label and description." }, "tag_ids": { "type": "array", "description": "Optional. Tag IDs. A schedule must carry **all** the tags in the list to be returned.", "items": { "type": "integer" } }, "types": { "type": "array", "description": "Optional. Default `[\"task\"]`. Which kind of schedule to return.", "items": { "type": "string", "enum": [ "task", "route" ] } } } } } } }, "responses": { "200": { "description": "Matching schedules", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/TaskScheduleEntry" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/schedule/read": { "post": { "tags": [ "Tasks" ], "summary": "Read task schedule", "description": "Return a task, route or checkpoint schedule by ID.\n\n`checkpoints` is present only when the ID belongs to a route schedule.\n\nNote the parameter is `id`, not `schedule_id`, unlike `task/schedule/delete`.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "taskScheduleRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of a task, route or checkpoint schedule.", "minimum": 1 } }, "required": [ "id" ] } } } }, "responses": { "200": { "description": "The schedule", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/TaskScheduleEntry" }, "checkpoints": { "type": "array", "description": "Every checkpoint schedule of the route schedule. Present only when the given ID is a route schedule.", "items": { "$ref": "#/components/schemas/CheckpointScheduleEntry" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/schedule/create": { "post": { "tags": [ "Tasks" ], "summary": "Create task schedule", "description": "Create a task schedule, and return its ID.\n\nJust after midnight in the user's own time zone, the platform checks every schedule and creates the tasks due that day, assigning them where an assignee is set.\n\nRequires the `task_update` right.", "operationId": "taskScheduleCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "schedule": { "allOf": [ { "$ref": "#/components/schemas/TaskScheduleEntry" } ], "description": "The schedule to create, without `id`. Read-only fields are ignored." } }, "required": [ "schedule" ] } } } }, "responses": { "200": { "description": "The created schedule", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new schedule entry." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not allow tasks.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: `schedule.form_template_id` names a form template that does not exist.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/schedule/update": { "post": { "tags": [ "Tasks" ], "summary": "Update task schedule", "description": "Update a task schedule. Pass a full schedule object including its `id`.\n\nChanging a schedule does not touch tasks it has already generated.\n\nRequires the `task_update` right.", "operationId": "taskScheduleUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "schedule": { "allOf": [ { "$ref": "#/components/schemas/TaskScheduleEntry" } ], "description": "The schedule to update, including its `id`. Read-only fields are ignored." } }, "required": [ "schedule" ] } } } }, "responses": { "200": { "description": "Schedule updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not allow tasks.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: no task schedule with this ID exists.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/schedule/delete": { "post": { "tags": [ "Tasks" ], "summary": "Delete task schedule", "description": "Delete a task schedule by ID. Tasks it has already generated are left alone.\n\nNote the parameter is `schedule_id`, not `id`, unlike `task/schedule/read`.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskScheduleDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "schedule_id": { "type": "integer", "description": "ID of the task schedule to delete.", "minimum": 1 } }, "required": [ "schedule_id" ] } } } }, "responses": { "200": { "description": "Schedule deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/schedule/proposal/list": { "post": { "tags": [ "Tasks" ], "summary": "List schedule proposals", "description": "Preview the tasks and routes the schedules would create over a period. **Creates nothing.**\n\n`from` should not be in the past, and `to` should not be before `from`.\n\nA request with an empty body reports `interval` as the missing parameter. That is the internal name of the field holding both dates; the public parameters are `from` and `to`.", "operationId": "taskScheduleProposalList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "from": { "type": "string", "description": "Show tasks that would be created after this date.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "Show tasks that would be created before this date. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "trackers": { "type": "array", "description": "Optional. IDs of the trackers the tasks would be assigned to.", "items": { "type": "integer" } }, "filter": { "type": "string", "description": "Optional. Filter over schedule label and description." }, "types": { "type": "array", "description": "Optional. Which kind of record to preview.", "items": { "type": "string", "enum": [ "task", "route" ] } } }, "required": [ "from", "to" ] } } } }, "responses": { "200": { "description": "The tasks and routes that would be created", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "description": "The proposed records, each shaped like the task it would become.", "items": { "$ref": "#/components/schemas/Task" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/schedule/route/create": { "post": { "tags": [ "Tasks" ], "summary": "Create route schedule", "description": "Create a route schedule together with its checkpoint schedules, and return its ID.\n\n**At least 2 checkpoints are required**, the same rule as a real route.\n\nRequires the `task_update` right.", "operationId": "taskScheduleRouteCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "route": { "allOf": [ { "$ref": "#/components/schemas/RouteScheduleEntry" } ], "description": "The route schedule to create, without `id`." }, "checkpoints": { "type": "array", "description": "The checkpoint schedules, in order of execution. At least 2.", "items": { "$ref": "#/components/schemas/CheckpointScheduleEntry" }, "minItems": 2 } }, "required": [ "route", "checkpoints" ] } } } }, "responses": { "200": { "description": "The created route schedule", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new route schedule entry." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/schedule/route/update": { "post": { "tags": [ "Tasks" ], "summary": "Update route schedule", "description": "Update a route schedule and its checkpoint schedules.\n\nA checkpoint carrying an `id` is updated, one without an `id` is created, and **any existing checkpoint absent from the request is deleted**. When `checkpoints` is given it must still hold at least 2.\n\nRequires the `task_update` right.", "operationId": "taskScheduleRouteUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "route": { "allOf": [ { "$ref": "#/components/schemas/RouteScheduleEntry" } ], "description": "The route schedule to update, including its `id`." }, "checkpoints": { "type": "array", "description": "The checkpoint schedules this route should end up with. Anything absent is deleted.", "items": { "$ref": "#/components/schemas/CheckpointScheduleEntry" }, "minItems": 2 } }, "required": [ "route" ] } } } }, "responses": { "200": { "description": "Route schedule updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/schedule/route/delete": { "post": { "tags": [ "Tasks" ], "summary": "Delete route schedule", "description": "Delete a route schedule **and its checkpoint schedules** by ID.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskScheduleRouteDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the route schedule.", "minimum": 1 } }, "required": [ "id" ] } } } }, "responses": { "200": { "description": "Route schedule deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/schedule/checkpoint/delete": { "post": { "tags": [ "Tasks" ], "summary": "Delete schedule checkpoint", "description": "Delete one checkpoint from a route schedule and reorder the rest.\n\n**A route schedule must keep at least 2 checkpoints.** When only two are left, use `task/schedule/checkpoint/transmute` on the other one instead of deleting this one.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskScheduleCheckpointDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "checkpoint_id": { "type": "integer", "description": "ID of the checkpoint schedule.", "minimum": 1 } }, "required": [ "checkpoint_id" ] } } } }, "responses": { "200": { "description": "Checkpoint schedule deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/task/schedule/checkpoint/transmute": { "post": { "tags": [ "Tasks" ], "summary": "Convert schedule checkpoint to task", "description": "Convert a checkpoint schedule into a task schedule, **deleting its route schedule and every other checkpoint on it**.\n\nThis is the way out when a route schedule is down to two checkpoints and one is no longer wanted.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `task_update` right.", "operationId": "taskScheduleCheckpointTransmute", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "checkpoint_id": { "type": "integer", "description": "ID of the checkpoint schedule.", "minimum": 1 } }, "required": [ "checkpoint_id" ] } } } }, "responses": { "200": { "description": "Checkpoint schedule converted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/read": { "post": { "tags": [ "Places" ], "summary": "Read place", "description": "Return one point of interest by ID.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "placeRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "place_id": { "type": "integer", "description": "ID of the POI.", "minimum": 1 } }, "required": [ "place_id" ] } } } }, "responses": { "200": { "description": "The POI", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Place" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/list": { "post": { "tags": [ "Places" ], "summary": "List places", "description": "List the points of interest of the current user, with filtering, sorting and pagination.\n\nCallable with an empty body. Also accepts GET with the same parameters as query-string values.", "operationId": "placeList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "place_ids": { "type": "array", "description": "Optional. Return only these POIs.", "items": { "type": "integer" } }, "filter": { "type": "string", "description": "Optional. Filter over all built-in and custom fields. Used together with `conditions`: both must match for a POI to be returned." }, "conditions": { "type": "array", "description": "Optional. Search conditions to apply. See the search conditions reference.", "items": { "type": "object", "additionalProperties": true } }, "order_by": { "type": "string", "description": "Optional. Field to sort by. Either a built-in name such as `label`, or a custom field ID as a string such as `\"123\"`." }, "ascending": { "type": "boolean", "description": "Optional. Sort direction. `false` sorts descending." }, "limit": { "type": "integer", "description": "Optional. Number of POIs to return.", "minimum": 0 }, "offset": { "type": "integer", "description": "Optional. Default 0. Offset into the results, for pagination.", "minimum": 0 }, "tag_ids": { "type": "array", "description": "Optional. Tag IDs. A POI must carry **all** the tags in the list to be returned.", "items": { "type": "integer" } } } } } } }, "responses": { "200": { "description": "Matching POIs", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Place" } }, "count": { "type": "integer", "description": "Total number of POIs found, ignoring pagination." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/create": { "post": { "tags": [ "Places" ], "summary": "Create place", "description": "Create a point of interest, and return its ID.\n\n`icon_id` cannot be set here: it is changed through `place/avatar/assign`.\n\nRequires the `place_update` right.", "operationId": "placeCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "place": { "allOf": [ { "$ref": "#/components/schemas/Place" } ], "description": "The POI to create, without the `id` field." }, "ignore_missing_fields": { "type": "boolean", "description": "Optional. Default `false`. When `true`, the POI is created even if a required custom field is missing." } }, "required": [ "place" ] } } } }, "responses": { "200": { "description": "The created POI", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new POI." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 268: the user's quota for POIs is exhausted.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/update": { "post": { "tags": [ "Places" ], "summary": "Update place", "description": "Update a point of interest. Pass a full place object including its `id`.\n\n**The avatar cannot be changed here.** Use `place/avatar/assign` for a preset icon, or `place/avatar/upload` for an image.\n\nRequires the `place_update` right.", "operationId": "placeUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "place": { "allOf": [ { "$ref": "#/components/schemas/Place" } ], "description": "The POI to update, including its `id`." }, "ignore_missing_fields": { "type": "boolean", "description": "Optional. Default `false`. When `true`, the POI is updated even if a required custom field is missing." } }, "required": [ "place" ] } } } }, "responses": { "200": { "description": "POI updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/delete": { "post": { "tags": [ "Places" ], "summary": "Delete place", "description": "Delete a point of interest by ID.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `place_update` right.", "operationId": "placeDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "place_id": { "type": "integer", "description": "ID of the POI to delete.", "minimum": 1 } }, "required": [ "place_id" ] } } } }, "responses": { "200": { "description": "POI deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/search_location": { "post": { "tags": [ "Places" ], "summary": "Search places by location", "description": "Return the ID and name of every point of interest whose zone contains the given coordinates.\n\nOnly `lat` and `lng` are used, so no radius is needed on the request.", "operationId": "placeSearchLocation", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "location": { "type": "object", "description": "The point to test.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } } }, "required": [ "location" ] } } } }, "responses": { "200": { "description": "POIs containing the point", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of a POI containing the searched location." }, "label": { "type": "string", "description": "Name of that POI." } } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/batch_convert": { "post": { "tags": [ "Places" ], "summary": "Convert place batch", "description": "Parse a tab-delimited batch of points of interest and return them checked, with per-row errors. **Creates nothing.**\n\nGive either `batch` or `file_id`. **If `file_id` is set, `batch` is ignored.** A request with neither returns error 7 with the message \"The 'batch' or 'file_id' parameter must be specified\".\n\nRequires the `place_update` right.", "operationId": "placeBatchConvert", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "batch": { "type": "string", "description": "Batch of tab-delimited POIs." }, "file_id": { "type": "string", "description": "Preloaded file ID. Takes precedence over `batch`." }, "fields": { "type": "array", "description": "Optional. Column names, in order. Defaults to `[\"label\", \"address\", \"lat\", \"lng\", \"radius\", \"description\", \"tags\"]`.", "items": { "type": "string" } }, "geocoder": { "type": "string", "description": "Geocoder type to use for resolving addresses." }, "default_radius": { "type": "integer", "description": "Optional. Default 100. Zone radius in metres for a row that gives no `radius`." } } } } } }, "responses": { "200": { "description": "The checked batch", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/CheckedPlace" } }, "limit_exceeded": { "type": "boolean", "description": "`true` if the given batch was constrained by a limit." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/upload": { "post": { "tags": [ "Places" ], "summary": "Upload places", "description": "Create points of interest in bulk from a CSV file.\n\n**This is a multipart request**, not JSON: `multipart/form-data` with the CSV in a part named `file`.\n\nThe CSV separator is a semicolon and a header row is required, naming `label;address;lat;lng;radius;external_id;description`.\n\nIf `redirect_target` is passed, the response is a redirect to that URL with `response=` appended.\n\nRequires the `place_update` right.", "operationId": "placeUpload", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "file": { "type": "string", "description": "The CSV file of POIs.", "format": "binary" }, "error_policy": { "type": "string", "description": "What to do with a row that fails validation.", "enum": [ "ignore", "fail" ] }, "duplicate_policy": { "type": "string", "description": "What to do when an uploaded POI carries an `external_id` that already exists. **Applies to `external_id` duplicates only.**", "enum": [ "skip", "update", "fail" ] }, "default_radius": { "type": "integer", "description": "Optional. Default 100. Zone radius in metres for a row that gives no radius." }, "geocoder": { "type": "string", "description": "Geocoder type to use for resolving addresses." }, "redirect_target": { "type": "string", "description": "Optional. URL to redirect to after the upload." } }, "required": [ "file" ] } } } }, "responses": { "200": { "description": "The upload result", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "total": { "type": "integer", "description": "Number of POIs processed." }, "errors": { "type": "integer", "description": "Number of rows that failed." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 234: the file could not be parsed. Error 268: the user's quota for POIs is exhausted.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 247: an uploaded POI carries an `external_id` that already exists and `duplicate_policy` is `fail`.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/avatar/assign": { "post": { "tags": [ "Places" ], "summary": "Assign place icon", "description": "Assign an icon from the standard icon set to a point of interest.\n\nSet `icon_id` to null to use an uploaded avatar instead of an icon.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `place_update` right.", "operationId": "placeAvatarAssign", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "place_id": { "type": "integer", "description": "ID of the POI.", "minimum": 1 }, "icon_id": { "type": [ "integer", "null" ], "description": "Optional. ID of the icon, 1 to 255. Null means use the uploaded avatar instead.", "minimum": 1, "maximum": 255 } }, "required": [ "place_id" ] } } } }, "responses": { "200": { "description": "Icon assigned", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/avatar/upload": { "post": { "tags": [ "Places" ], "summary": "Upload place avatar", "description": "Upload an avatar image for a point of interest.\n\n**This is a multipart request**, not JSON: `multipart/form-data` with the image in a part named `file`. The file MIME type must be one of `image/jpeg`, `image/pjpeg`, `image/png`, `image/gif`, or `image/webp`.\n\nThe uploaded image becomes available at `//place/avatars/`, and the returned name is also what `place/list` reports as `avatar_file_name`.\n\nIf `redirect_target` is passed, the response is a redirect to that URL with `response=` appended.\n\nRequires the `place_update` right.", "operationId": "placeAvatarUpload", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "place_id": { "type": "integer", "description": "ID of the POI." }, "file": { "type": "string", "description": "The image file.", "format": "binary" }, "redirect_target": { "type": "string", "description": "Optional. URL to redirect to after the upload." } }, "required": [ "place_id", "file" ] } } } }, "responses": { "200": { "description": "The stored avatar", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "string", "description": "Avatar file name." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 233: the file part is missing. Error 234: the file has an unexpected MIME type. Error 254: the file could not be saved.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/import/start": { "post": { "tags": [ "Places" ], "summary": "Start POI import", "description": "Start the background process that imports points of interest from a preloaded TSV file, and return the process ID.\n\nCreate the file first with `data/spreadsheet/parse`, which converts a spreadsheet to TSV and returns its name as `file_id`. Pass that name here as `filename`.\n\nAddress and coordinates complete each other: given only an address the coordinates come from geocoding, and given only coordinates the address comes from reverse geocoding.\n\nTags that do not exist yet are created automatically, within the user's tag quota.\n\nA POI is not imported when a required custom field is missing or invalid. An invalid value in a custom field that is not required is skipped with a warning.\n\nRequires the `place_update` right.", "operationId": "placeImportStart", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "filename": { "type": "string", "description": "Name of the server-side TSV file, as returned by `data/spreadsheet/parse` in its `file_id`. Must not be empty.", "minLength": 1 }, "headers": { "type": "array", "description": "What each column of the file holds, in order. Must not be empty. Accepted names are `label`, `address`, `lat`, `lng`, `radius` which defaults to 100, `description`, `tags`, a custom field ID as a string such as `\"131312\"`, and `undefined` for a column whose meaning is not known.\n\nFor custom fields of type `employee` and `multi_employee` the value is matched against the user's employees by full name. A `multi_employee` field takes several names separated by commas or semicolons.", "items": { "type": "string" }, "minItems": 1 }, "user_headers": { "type": "array", "description": "Optional. Display labels for the columns. Must be the same length as `headers`.", "items": { "type": "string" } } }, "required": [ "filename", "headers" ] } } } }, "responses": { "200": { "description": "The started import", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the created import process." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 233: the preloaded file was not found. Error 234: the file is not a TSV.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 247: an identical import already exists for the same file.", "$ref": "#/components/responses/ResponseError" }, "429": { "description": "Error 15: too many requests, when too many imports are already in progress.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/import/read": { "post": { "tags": [ "Places" ], "summary": "Read POI import", "description": "Return one POI import process by ID, including its progress.", "operationId": "placeImportRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "process_id": { "type": "integer", "description": "ID of the import process.", "minimum": 1 } }, "required": [ "process_id" ] } } } }, "responses": { "200": { "description": "The import process", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/ImportProcess" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/import/list": { "post": { "tags": [ "Places" ], "summary": "List POI imports", "description": "List the current user's **unfinished** POI import processes, meaning those with status `created`, `in_progress`, `done` or `failed`.\n\nA process marked finished with `place/import/finish` drops out of this list.\n\nCallable with an empty body.", "operationId": "placeImportList", "responses": { "200": { "description": "Unfinished import processes", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/ImportProcess" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/import/download_failed": { "post": { "tags": [ "Places" ], "summary": "Download failed POI rows", "description": "Return a file of the rows that failed validation, so they can be corrected and imported again.\n\nThe response is the file itself, not a JSON envelope.", "operationId": "placeImportDownloadFailed", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "process_id": { "type": "integer", "description": "ID of the import process.", "minimum": 1 } }, "required": [ "process_id" ] } } } }, "responses": { "200": { "description": "The failed rows as a file", "content": { "text/tab-separated-values": { "schema": { "type": "string", "format": "binary" } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: the file of failed rows was not found.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/place/import/finish": { "post": { "tags": [ "Places" ], "summary": "Finish POI import", "description": "Mark an import process as finished, which drops it out of `place/import/list`.\n\n**Only a process that has stopped can be finished.** One still running returns error 280.", "operationId": "placeImportFinish", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "process_id": { "type": "integer", "description": "ID of the import process.", "minimum": 1 } }, "required": [ "process_id" ] } } } }, "responses": { "200": { "description": "Import marked finished", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/form/read": { "post": { "tags": [ "Forms" ], "summary": "Read form", "description": "Return a form by ID, together with the files used in its values.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "formRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the form.", "minimum": 1 } }, "required": [ "id" ] } } } }, "responses": { "200": { "description": "The form and its files", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Form" }, "files": { "type": "array", "description": "Files used in the values of this form. May be null or empty.", "items": { "$ref": "#/components/schemas/FormFile" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/form/download": { "post": { "tags": [ "Forms" ], "summary": "Download form", "description": "Return a form rendered as a file.\n\nThe response is the file itself, not a JSON envelope, unless an error occurs.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "formDownload", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the form.", "minimum": 1 }, "format": { "type": "string", "description": "File format of the rendered form.", "enum": [ "pdf", "xlsx" ] } }, "required": [ "id", "format" ] } } } }, "responses": { "200": { "description": "The rendered form file", "content": { "application/pdf": { "schema": { "type": "string", "format": "binary" } }, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { "schema": { "type": "string", "format": "binary" } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/form/template/list": { "post": { "tags": [ "Forms" ], "summary": "List form templates", "description": "List the form templates of the current user.\n\nCallable with an empty body. Also accepts GET.", "operationId": "formTemplateList", "responses": { "200": { "description": "The templates", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/FormTemplate" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/form/template/read": { "post": { "tags": [ "Forms" ], "summary": "Read form template", "description": "Return one form template by ID.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "formTemplateRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "template_id": { "type": "integer", "description": "ID of the form template.", "minimum": 1 } }, "required": [ "template_id" ] } } } }, "responses": { "200": { "description": "The template", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/FormTemplate" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/form/template/create": { "post": { "tags": [ "Forms" ], "summary": "Create form template", "description": "Create a form template, and return its ID.\n\nA form is a one-shot entity: once someone fills it in, it is stored with its values for reference and cannot be reused. Templates exist so the same set of fields can be issued over and over, and so that changing the template later does not alter forms already filled in.\n\nRequires the `form_template_update` right.", "operationId": "formTemplateCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "template": { "allOf": [ { "$ref": "#/components/schemas/FormTemplate" } ], "description": "The template to create, without `id`, `created` or `updated`. **`default` must be sent**, including as `false`: omitting it fails with error 7 and `template.default must not be null`. Each entry in `fields` also carries required properties that depend on its `type`, for example `max_length` on a text field; the form field types reference documents them per type." } }, "required": [ "template" ] } } } }, "responses": { "200": { "description": "The created template", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new form template." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 101: the current user has the demo flag, and this operation is disabled in demo mode.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/form/template/update": { "post": { "tags": [ "Forms" ], "summary": "Update form template", "description": "Update a form template. Pass a full template object including its `id`.\n\n**Forms already created from this template are not changed.** Each keeps the fields it was issued with, which is the reason templates and forms are separate entities. `version` increments on every modification.\n\nRequires the `form_template_update` right.", "operationId": "formTemplateUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "template": { "allOf": [ { "$ref": "#/components/schemas/FormTemplate" } ], "description": "The template to update, including its `id`." } }, "required": [ "template" ] } } } }, "responses": { "200": { "description": "Template updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 101: the current user has the demo flag, and this operation is disabled in demo mode.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/form/template/delete": { "post": { "tags": [ "Forms" ], "summary": "Delete form template", "description": "Delete a form template by ID.\n\nForms already created from it survive, with `template_id` set to null.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `form_template_update` right.", "operationId": "formTemplateDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "template_id": { "type": "integer", "description": "ID of the form template to delete.", "minimum": 1 } }, "required": [ "template_id" ] } } } }, "responses": { "200": { "description": "Template deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 101: the current user has the demo flag, and this operation is disabled in demo mode.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/form/template/stats/read": { "post": { "tags": [ "Forms" ], "summary": "Read template usage", "description": "Return how many tasks and task schedules use a form template.\n\nUseful before deleting a template, to see what is still relying on it.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "formTemplateStatsRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "template_id": { "type": "integer", "description": "ID of the form template.", "minimum": 1 } }, "required": [ "template_id" ] } } } }, "responses": { "200": { "description": "The usage counts", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "tasks": { "type": "object", "description": "How many tasks use this template, broken down by task status.", "properties": { "unassigned": { "type": "integer", "description": "Tasks not assigned to anyone." }, "assigned": { "type": "integer", "description": "Tasks assigned to a performer." }, "done": { "type": "integer", "description": "Completed tasks." }, "failed": { "type": "integer", "description": "Failed tasks." }, "delayed": { "type": "integer", "description": "Delayed tasks." }, "arrived": { "type": "integer", "description": "Tasks whose performer reached the zone without completing them." }, "faulty": { "type": "integer", "description": "Tasks completed with problems." } } }, "scheduled": { "type": "integer", "description": "Number of task schedules using this template." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/employee/list": { "post": { "tags": [ "Employees" ], "summary": "List employees", "description": "List the employees and drivers of the current user, with pagination, sorting and a text filter.\n\nCallable with an empty body. Also accepts GET with the same parameters as query-string values.", "operationId": "employeeList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "limit": { "type": "integer", "description": "Pagination. Maximum number of employee records to return." }, "offset": { "type": "integer", "description": "Pagination. Return employees starting from this offset." }, "sort": { "type": "array", "description": "Optional. Sort options, each a property and direction such as `first_name=desc`. **At most 2 options per request.** Sortable properties: `id`, `first_name`, `object_label`, `department_label`, `personnel_number`, `hardware_key`, `phone`, `email`, `address`, `driver_license_number`, `driver_license_cats`, `driver_license_valid_till`, and `ssn`.", "items": { "type": "string" }, "maxItems": 2 }, "filter": { "type": "string", "description": "Optional. Text filter. An employee matches when at least one of the sortable properties above contains the string. Up to 100 characters, or null.", "maxLength": 100 } } } } } }, "responses": { "200": { "description": "Matching employees", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Employee" } }, "count": { "type": "integer", "description": "Total number of employees, ignoring pagination." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/employee/read": { "post": { "tags": [ "Employees" ], "summary": "Read employee", "description": "Return one employee or driver by ID.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "employeeRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "employee_id": { "type": "integer", "description": "ID of the employee.", "minimum": 1 } }, "required": [ "employee_id" ] } } } }, "responses": { "200": { "description": "The employee", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Employee" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/employee/create": { "post": { "tags": [ "Employees" ], "summary": "Create employee", "description": "Create an employee or driver, and return the ID.\n\n`icon_id` and `avatar_file_name` cannot be set here: they are changed through `employee/avatar/assign` and `employee/avatar/upload`.\n\nRequires the `employee_update` right.", "operationId": "employeeCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "employee": { "allOf": [ { "$ref": "#/components/schemas/Employee" } ], "description": "The employee to create. `id` may be null or absent." } }, "required": [ "employee" ] } } } }, "responses": { "200": { "description": "The created employee", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new employee." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 247: an employee with this `hardware_key` or another unique value already exists.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/employee/update": { "post": { "tags": [ "Employees" ], "summary": "Update employee", "description": "Update an employee or driver. Pass a full employee object including its `id`.\n\n`icon_id` and `avatar_file_name` cannot be set here: they are changed through `employee/avatar/assign` and `employee/avatar/upload`.\n\nRequires the `employee_update` right.", "operationId": "employeeUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "employee": { "allOf": [ { "$ref": "#/components/schemas/Employee" } ], "description": "The employee to update, including its `id`." } }, "required": [ "employee" ] } } } }, "responses": { "200": { "description": "Employee updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 247: an employee with this `hardware_key` or another unique value already exists.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/employee/delete": { "post": { "tags": [ "Employees" ], "summary": "Delete employee", "description": "Delete an employee or driver by ID.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `employee_update` right.", "operationId": "employeeDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "employee_id": { "type": "integer", "description": "ID of the employee to delete.", "minimum": 1 } }, "required": [ "employee_id" ] } } } }, "responses": { "200": { "description": "Employee deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/employee/batch_convert": { "post": { "tags": [ "Employees" ], "summary": "Convert employee batch", "description": "Parse a tab-delimited batch of employees and return them checked, with per-row errors. **Creates nothing.**\n\nGive either `batch` or `file_id`. **If `file_id` is set, `batch` is ignored.** A request with neither returns error 7 with the message \"The 'batch' or 'file_id' parameter must be specified\".\n\nRequires the `employee_update` right.", "operationId": "employeeBatchConvert", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "batch": { "type": "string", "description": "Batch of tab-delimited employees." }, "file_id": { "type": "string", "description": "Preloaded file ID. Takes precedence over `batch`." }, "fields": { "type": "array", "description": "Optional. Column names, in order. Defaults to `[\"first_name\", \"middle_name\", \"last_name\", \"email\", \"phone\"]`.", "items": { "type": "string" } }, "geocoder": { "type": "string", "description": "Geocoder type to use for resolving addresses." }, "default_radius": { "type": "integer", "description": "Optional. Default 100. Radius in metres for a row that gives no radius." } } } } } }, "responses": { "200": { "description": "The checked batch", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/CheckedEmployee" } }, "limit_exceeded": { "type": "boolean", "description": "`true` if the given batch was constrained by a limit." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 234: the batch or file could not be parsed.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/employee/avatar/assign": { "post": { "tags": [ "Employees" ], "summary": "Assign employee icon", "description": "Assign an icon from the standard icon set to an employee.\n\nSet `icon_id` to null to use an uploaded avatar instead of an icon.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `employee_update` right.", "operationId": "employeeAvatarAssign", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "employee_id": { "type": "integer", "description": "ID of the employee.", "minimum": 1 }, "icon_id": { "type": [ "integer", "null" ], "description": "Optional. ID of the icon. Null means use the uploaded avatar instead." } }, "required": [ "employee_id" ] } } } }, "responses": { "200": { "description": "Icon assigned", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/employee/avatar/upload": { "post": { "tags": [ "Employees" ], "summary": "Upload employee avatar", "description": "Upload an avatar image for an employee.\n\n**This is a multipart request**, not JSON: `multipart/form-data` with the image in a part named `file`.\n\nThe returned name is what `employee/list` reports as `avatar_file_name`.\n\nIf `redirect_target` is passed, the response is a redirect to that URL with `response=` appended.\n\nRequires the `employee_update` right.", "operationId": "employeeAvatarUpload", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "employee_id": { "type": "integer", "description": "ID of the employee." }, "file": { "type": "string", "description": "The image file.", "format": "binary" }, "redirect_target": { "type": "string", "description": "Optional. URL to redirect to after the upload." } }, "required": [ "employee_id", "file" ] } } } }, "responses": { "200": { "description": "The stored avatar", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "string", "description": "Avatar file name." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 233: the file part is missing. Error 234: the file has an unexpected MIME type. Error 254: the file could not be saved.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/employee/import/start": { "post": { "tags": [ "Employees" ], "summary": "Start employee import", "description": "Start the background process that imports employees from a preloaded TSV file, and return the process ID.\n\nCreate the file first with `data/spreadsheet/parse`, which converts a spreadsheet to TSV and returns its name as `file_id`. Pass that name here as `filename`.\n\nRequires the `employee_update` right.", "operationId": "employeeImportStart", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "filename": { "type": "string", "description": "Name of the server-side TSV file, as returned by `data/spreadsheet/parse` in its `file_id`. Must not be empty.", "minLength": 1 }, "headers": { "type": "array", "description": "What each column of the file holds, in order. Must not be empty. Use `undefined` for a column whose meaning is not known.", "items": { "type": "string" }, "minItems": 1 }, "user_headers": { "type": "array", "description": "Optional. Display labels for the columns. Must be the same length as `headers`.", "items": { "type": "string" } } }, "required": [ "filename", "headers" ] } } } }, "responses": { "200": { "description": "The started import", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the created import process." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 233: the preloaded file was not found. Error 234: the file is not a TSV.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 247: an identical import already exists for the same file.", "$ref": "#/components/responses/ResponseError" }, "429": { "description": "Error 15: too many requests, when too many imports are already in progress.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/employee/import/read": { "post": { "tags": [ "Employees" ], "summary": "Read employee import", "description": "Return one employee import process by ID, including its progress.\n\nThe import process object is the same shape every import family returns, differing only in `type`, which is `employee` here.", "operationId": "employeeImportRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "process_id": { "type": "integer", "description": "ID of the import process.", "minimum": 1 } }, "required": [ "process_id" ] } } } }, "responses": { "200": { "description": "The import process", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/ImportProcess" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/employee/import/list": { "post": { "tags": [ "Employees" ], "summary": "List employee imports", "description": "List the current user's unfinished employee import processes.\n\nCallable with an empty body.", "operationId": "employeeImportList", "responses": { "200": { "description": "Unfinished import processes", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/ImportProcess" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/employee/import/download_failed": { "post": { "tags": [ "Employees" ], "summary": "Download failed employee rows", "description": "Return a file of the rows that failed validation, so they can be corrected and imported again.\n\nThe response is the file itself, not a JSON envelope.", "operationId": "employeeImportDownloadFailed", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "process_id": { "type": "integer", "description": "ID of the import process.", "minimum": 1 } }, "required": [ "process_id" ] } } } }, "responses": { "200": { "description": "The failed rows as a file", "content": { "text/tab-separated-values": { "schema": { "type": "string", "format": "binary" } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: the file of failed rows was not found.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/department/list": { "post": { "tags": [ "Employees" ], "summary": "List departments", "description": "List all departments belonging to the current user.\n\nCallable with an empty body. Also accepts GET.", "operationId": "departmentList", "responses": { "200": { "description": "The departments", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Department" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/department/create": { "post": { "tags": [ "Employees" ], "summary": "Create department", "description": "Create a department, and return its ID.\n\nRequires the `employee_update` right.", "operationId": "departmentCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "department": { "allOf": [ { "$ref": "#/components/schemas/Department" } ], "description": "The department to create, without the `id` field." } }, "required": [ "department" ] } } } }, "responses": { "200": { "description": "The created department", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new department." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/department/update": { "post": { "tags": [ "Employees" ], "summary": "Update department", "description": "Update a department. Pass a full department object including its `id`.\n\nRequires the `employee_update` right.", "operationId": "departmentUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "department": { "allOf": [ { "$ref": "#/components/schemas/Department" } ], "description": "The department to update, including its `id`." } }, "required": [ "department" ] } } } }, "responses": { "200": { "description": "Department updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/department/delete": { "post": { "tags": [ "Employees" ], "summary": "Delete department", "description": "Delete a department by ID.\n\nEmployees assigned to it are not deleted; their `department_id` becomes null.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `employee_update` right.", "operationId": "departmentDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "department_id": { "type": "integer", "description": "ID of the department to delete.", "minimum": 1 } }, "required": [ "department_id" ] } } } }, "responses": { "200": { "description": "Department deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/checkin/read": { "post": { "tags": [ "Check-ins" ], "summary": "Read check-in", "description": "Return one check-in by ID.\n\nCheck-ins are created from the Mobile Tracker App, not from this API, so the check-in operations are read-only apart from the exceptional-case create.\n\n**No specific right is required**, only a valid session.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "checkinRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "checkin_id": { "type": "integer", "description": "ID of the check-in.", "minimum": 1 } }, "required": [ "checkin_id" ] } } } }, "responses": { "200": { "description": "The check-in", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Checkin" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: no check-in with this ID belongs to the authorized user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/checkin/list": { "post": { "tags": [ "Check-ins" ], "summary": "List check-ins", "description": "Return check-ins for a set of trackers over a period, as JSON or as a report file.\n\n**Setting `format` changes the response to a file download** rather than JSON, which is the only way this operation differs from a plain list.\n\n**No specific right is required**, only a valid session.", "operationId": "checkinList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Optional. Tracker IDs. None may be deleted or blocked. Without this, every available tracker is used.", "items": { "type": "integer" } }, "from": { "type": "string", "description": "Optional. Start of the period.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "Optional. End of the period. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "conditions": { "type": "array", "description": "Optional. Search conditions to apply. Conditions may be built on `employee` (an ID), `tracker_id`, `marker_time`, `location` (the address), `comment`, and `form` (the template ID).", "items": { "type": "object", "additionalProperties": true } }, "sort": { "type": "array", "description": "Optional. Sort options, each a property and direction such as `marker_time=desc`. Sortable properties: `employee` (full name), `employee_fullname`, `tracker_id`, `marker_time`, `location`, `address`, `comment`, `form`, and `form_label`.", "items": { "type": "string" } }, "location": { "type": "object", "description": "Optional. Return only check-ins inside this circle.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "radius": { "type": "integer", "description": "Radius in metres." } } }, "limit": { "type": "integer", "description": "Optional. Maximum number of records to return." }, "offset": { "type": "integer", "description": "Optional. Default 0. Offset into the results, for pagination.", "minimum": 0 }, "format": { "type": "string", "description": "Optional. Leave it out for JSON. Setting it returns a file download instead.", "enum": [ "pdf", "xlsx" ] } } } } } }, "responses": { "200": { "description": "Matching check-ins, when `format` is not set", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Checkin" } }, "count": { "type": "integer", "description": "Total number of check-ins, ignoring pagination." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 211: the requested time span is too big. Error 217: one of the given trackers does not exist, is blocked, or lacks the required plan features. Error 221: the device limit set for the user's dealer has been exceeded.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/checkin/create": { "post": { "tags": [ "Check-ins" ], "summary": "Create check-in", "description": "Create a check-in. **For exceptional cases only**: check-ins normally come from the Mobile Tracker App.\n\nTo send a form with the check-in, create the form first with `checkin/form/create` and pass its ID in `form_submission`. Leave optional form fields out of `values` entirely rather than sending them empty.\n\nFiles come from `checkin/image/create`, whose returned IDs go in `file_ids`.", "operationId": "checkinCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 }, "location": { "type": "object", "description": "Where the check-in happened.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address at that point." } } }, "comment": { "type": "string", "description": "Optional. A comment on the check-in." }, "file_ids": { "type": "array", "description": "Optional. IDs of files created with `checkin/image/create`.", "items": { "type": "integer" } }, "form_submission": { "type": "object", "description": "Optional. A form to send with the check-in.", "properties": { "form_id": { "type": "integer", "description": "ID of a form previously created with `checkin/form/create`." }, "values": { "type": "object", "description": "Values for the form fields, keyed by field ID. Omit optional fields entirely rather than sending them empty.", "additionalProperties": true } } } }, "required": [ "tracker_id", "location" ] } } } }, "responses": { "200": { "description": "The created check-in", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new check-in." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/checkin/delete": { "post": { "tags": [ "Check-ins" ], "summary": "Delete check-ins", "description": "Delete check-ins by ID.\n\nRequires the `checkin_update` right, and a caller without it gets error 13. **No security group grants that right.** It follows from the `fsm` platform feature together with the `checkin` tariff feature on at least one tracker, so on an account with both every sub-user holds it, and on an account missing either nobody does, the master included.\n\nCheck-ins can therefore exist on an account that cannot delete them: `checkin/create` requires no right at all.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "checkinDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "checkin_ids": { "type": "array", "description": "IDs of the check-ins to delete.", "items": { "type": "integer" } } }, "required": [ "checkin_ids" ] } } } }, "responses": { "200": { "description": "Check-ins deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller does not hold the `checkin_update` right.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/checkin/image/create": { "post": { "tags": [ "Check-ins" ], "summary": "Create check-in image", "description": "Reserve a file for a check-in and return the credentials for uploading it.\n\nThis does not upload anything. It reserves space in the account's file quota and returns where and how to POST the file. Pass the returned `file_id` in `checkin/create`.\n\n**Leave a brief delay between uploads** when sending several files.", "operationId": "checkinImageCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "size": { "type": "integer", "description": "Maximum size in bytes of the file to be uploaded, used to reserve quota. The platform default cap is 16 MB.", "minimum": 1 }, "filename": { "type": "string", "description": "Optional. Name to give the uploaded file. Without it the name comes from the upload itself." }, "metadata": { "type": "object", "description": "Optional. Metadata for the file, for images only.", "additionalProperties": true } }, "required": [ "size" ] } } } }, "responses": { "200": { "description": "Where to upload the file", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/FileUploadTarget" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 268: the file cannot be created without violating the account's file quota. Error 271: `size` is above the maximum allowed, 16 MB by default.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/checkin/form/create": { "post": { "tags": [ "Check-ins" ], "summary": "Create check-in form", "description": "Create a form that can be sent with a check-in, and return its ID. A form is always created from a form template.\n\nPass the returned ID as `form_submission.form_id` in `checkin/create`.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "checkinFormCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 }, "template_id": { "type": "integer", "description": "ID of the form template to build the form from.", "minimum": 1 } }, "required": [ "tracker_id", "template_id" ] } } } }, "responses": { "200": { "description": "The created form", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new form." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/checkin/form/file/create": { "post": { "tags": [ "Check-ins" ], "summary": "Create check-in form file", "description": "Reserve a file against a check-in form field and return the credentials for uploading it.\n\n**Give either `checkin_id` or `form_id`, not both.** A request with neither reports a missing parameter named `has_at_least_one_id_field`, which is the internal name of the validation group rather than a parameter you can send; unusually for this API, it carries a readable message saying which two to choose between.\n\nThe upload works exactly as for `checkin/image/create`.\n\n**Leave a brief delay between uploads** when sending several files.", "operationId": "checkinFormFileCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "checkin_id": { "type": "integer", "description": "ID of the check-in the form is attached to. Use this or `form_id`, not both.", "minimum": 1 }, "form_id": { "type": "integer", "description": "ID of the form. Use this or `checkin_id`, not both.", "minimum": 1 }, "field_id": { "type": "string", "description": "ID of the form field the file attaches to. The field must be file-based." }, "size": { "type": "integer", "description": "Maximum size in bytes of the file to be uploaded, used to reserve quota. The platform default cap is 16 MB.", "minimum": 1 }, "filename": { "type": "string", "description": "Optional. Name to give the uploaded file. Without it the name comes from the upload itself." }, "metadata": { "type": "object", "description": "Optional. Metadata for the file, for images only.", "additionalProperties": true } }, "required": [ "field_id", "size" ] } } } }, "responses": { "200": { "description": "Where to upload the file", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/FileUploadTarget" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 268: the file cannot be created without violating the account's file quota. Error 271: `size` is above the maximum allowed, 16 MB by default.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 267: six or more unsubmitted files are already associated with this form field.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/list": { "post": { "tags": [ "Sub-users" ], "summary": "List sub-users", "description": "List the sub-users of the current master account.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nCallable with an empty body. Also accepts GET.\n\nRequires the `admin` right.", "operationId": "subuserList", "responses": { "200": { "description": "The sub-users", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Subuser" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/register": { "post": { "tags": [ "Sub-users" ], "summary": "Register sub-user", "description": "Create a sub-user under the current master account, and return its ID.\n\nA new sub-user starts with whatever security group is given in `security_group_id`. Null means the default group, which has **no rights at all**.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserRegister", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "user": { "allOf": [ { "$ref": "#/components/schemas/Subuser" } ], "description": "The sub-user to create, without the `id` field." }, "password": { "type": "string", "description": "Password for the new sub-user. Must hold at least one uppercase letter, one lowercase letter, one digit and one special character. The length limits and the accepted special characters are installation config, not platform constants, so read them from `paas_settings.password_policy` in `user/get_info` instead of assuming: the hosted platform asks for 12 to 20 characters. A password that fails the policy returns error 7 naming `password`." } }, "required": [ "user", "password" ] } } } }, "responses": { "200": { "description": "The created sub-user", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new sub-user." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 206: the login is already in use.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/update": { "post": { "tags": [ "Sub-users" ], "summary": "Update sub-user", "description": "Update a sub-user. Pass a full sub-user object including its `id`.\n\n`creation_date` is read-only and should not be sent.\n\nThe password is not changed here.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "user": { "allOf": [ { "$ref": "#/components/schemas/Subuser" } ], "description": "The sub-user to update, including its `id`." } }, "required": [ "user" ] } } } }, "responses": { "200": { "description": "Sub-user updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/delete": { "post": { "tags": [ "Sub-users" ], "summary": "Delete sub-user", "description": "Delete a sub-user. **This cannot be reversed.**\n\nEntities the sub-user created stay with the master account, since a sub-user owns nothing exclusively. The exception is reports: each sub-user has its own report pool and schedule.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `admin` right.", "operationId": "subuserDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 } }, "required": [ "subuser_id" ] } } } }, "responses": { "200": { "description": "Sub-user deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/session/create": { "post": { "tags": [ "Sub-users" ], "summary": "Create sub-user session", "description": "Open a session as one of your sub-users, and return its hash. This is how a master user works on a sub-user's behalf without knowing their password.\n\nThe returned hash is used exactly like one from `user/auth`, and carries that sub-user's rights and scoping.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `admin` right.", "operationId": "subuserSessionCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 } }, "required": [ "subuser_id" ] } } } }, "responses": { "200": { "description": "The new session", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "hash": { "type": "string", "description": "Session hash for the sub-user, to send in the `Authorization` header." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/security_group/list": { "post": { "tags": [ "Sub-users" ], "summary": "List security groups", "description": "List the security groups of the current master account.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nCallable with an empty body. Also accepts GET.\n\nRequires the `admin` right.", "operationId": "subuserSecurityGroupList", "responses": { "200": { "description": "The security groups", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/SecurityGroup" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/security_group/create": { "post": { "tags": [ "Sub-users" ], "summary": "Create security group", "description": "Create a security group, and return its ID.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserSecurityGroupCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "group": { "allOf": [ { "$ref": "#/components/schemas/SecurityGroup" } ], "description": "The security group to create, without the `id` field." } }, "required": [ "group" ] } } } }, "responses": { "200": { "description": "The created group", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new security group." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/security_group/update": { "post": { "tags": [ "Sub-users" ], "summary": "Update security group", "description": "Update a security group. Pass a full group object including its `id`.\n\n**The change takes effect for every sub-user in the group**, including sessions already open.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserSecurityGroupUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "group": { "allOf": [ { "$ref": "#/components/schemas/SecurityGroup" } ], "description": "The security group to update, including its `id`." } }, "required": [ "group" ] } } } }, "responses": { "200": { "description": "Security group updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/security_group/delete": { "post": { "tags": [ "Sub-users" ], "summary": "Delete security group", "description": "Delete a security group.\n\nSub-users in it fall back to the default group, which has no rights.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `admin` right.", "operationId": "subuserSecurityGroupDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "security_group_id": { "type": "integer", "description": "ID of the security group to delete.", "minimum": 1 } }, "required": [ "security_group_id" ] } } } }, "responses": { "200": { "description": "Security group deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/security_group/assign": { "post": { "tags": [ "Sub-users" ], "summary": "Assign security group", "description": "Put one or more sub-users into a security group.\n\n**Pass `group_id` as null to move them to the default group**, which has no rights. Only `subuser_ids` is required.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserSecurityGroupAssign", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "group_id": { "type": [ "integer", "null" ], "description": "ID of the security group. Null moves the sub-users to the default group." }, "subuser_ids": { "type": "array", "description": "IDs of the sub-users to assign. Must not be empty.", "items": { "type": "integer" }, "minItems": 1 } }, "required": [ "subuser_ids" ] } } } }, "responses": { "200": { "description": "Security group assigned", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/tracker/list": { "post": { "tags": [ "Sub-users" ], "summary": "List sub-user trackers", "description": "List the trackers a sub-user can see.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `admin` right.", "operationId": "subuserTrackerList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 } }, "required": [ "subuser_id" ] } } } }, "responses": { "200": { "description": "The trackers in the sub-user's scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "description": "IDs of the trackers available to the sub-user.", "items": { "type": "integer" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/tracker/bind": { "post": { "tags": [ "Sub-users" ], "summary": "Bind trackers to sub-user", "description": "Give a sub-user access to trackers.\n\nEverything associated with a tracker the sub-user cannot see is hidden from them too, which is what the introduction calls scoping.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `admin` right.", "operationId": "subuserTrackerBind", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 }, "trackers": { "type": "array", "description": "IDs of the trackers to associate with the sub-user. All must belong to the current master account.", "items": { "type": "integer" } } }, "required": [ "subuser_id", "trackers" ] } } } }, "responses": { "200": { "description": "Trackers bound", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user. Error 262: the trackers cannot be bound as requested.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/tracker/unbind": { "post": { "tags": [ "Sub-users" ], "summary": "Unbind trackers from sub-user", "description": "Take away a sub-user's access to trackers.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `admin` right.", "operationId": "subuserTrackerUnbind", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 }, "trackers": { "type": "array", "description": "IDs of the trackers to remove from the sub-user. All must belong to the current master account.", "items": { "type": "integer" } } }, "required": [ "subuser_id", "trackers" ] } } } }, "responses": { "200": { "description": "Trackers unbound", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user. Error 262: the trackers cannot be unbound as requested.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/places/bind": { "post": { "tags": [ "Sub-users" ], "summary": "Bind places to sub-user", "description": "Give a sub-user access to places, either a named set or all of them.\n\n**Give either `access_to_all` or `place_ids`.** A request with neither reports a missing parameter named `has_at_least_one_parameter`, which is the internal name of the validation group rather than a parameter you can send; it carries a message naming the two real choices.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserPlacesBind", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 }, "access_to_all": { "type": "boolean", "description": "When `true`, the sub-user gets access to every place of the master account. Use this or `place_ids`." }, "place_ids": { "type": "array", "description": "IDs of the places to associate with the sub-user. All must belong to the current master account. Use this or `access_to_all`.", "items": { "type": "integer" } } }, "required": [ "subuser_id" ] } } } }, "responses": { "200": { "description": "Places bound", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/places/unbind": { "post": { "tags": [ "Sub-users" ], "summary": "Unbind places from sub-user", "description": "Take away a sub-user's access to places.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserPlacesUnbind", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 }, "place_ids": { "type": "array", "description": "IDs of the places to remove from the sub-user. All must belong to the current master account.", "items": { "type": "integer" } } }, "required": [ "subuser_id", "place_ids" ] } } } }, "responses": { "200": { "description": "Places unbound", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/places/list_ids": { "post": { "tags": [ "Sub-users" ], "summary": "List sub-user place IDs", "description": "Return just the IDs of the places a sub-user can see.\n\nUse `subuser/places/list` when you want the place objects themselves.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserPlacesListIds", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 } }, "required": [ "subuser_id" ] } } } }, "responses": { "200": { "description": "The place IDs in the sub-user's scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "description": "IDs of the places available to the sub-user.", "items": { "type": "integer" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/places/list": { "post": { "tags": [ "Sub-users" ], "summary": "List sub-user places", "description": "Return the places a sub-user can see, as full place objects, with filtering and pagination.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserPlacesList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 }, "filter": { "type": "string", "description": "Optional. Filter over place label, description, address, external ID and custom fields." }, "tag_ids": { "type": "array", "description": "Optional. Tag IDs. A place must carry **all** the tags in the list to be returned.", "items": { "type": "integer" } }, "order": { "type": "string", "description": "Optional. Default `id`. How to order the list.", "enum": [ "id", "label", "description", "location", "external_id", "assigned_date" ] }, "limit": { "type": "integer", "description": "Optional. Maximum number of places to return." }, "offset": { "type": "integer", "description": "Optional. Default 0. Offset into the results, for pagination.", "minimum": 0 } }, "required": [ "subuser_id" ] } } } }, "responses": { "200": { "description": "The places in the sub-user's scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Place" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/zones/bind": { "post": { "tags": [ "Sub-users" ], "summary": "Bind geofences to sub-user", "description": "Give a sub-user access to geofences, either a named set or all of them.\n\n**Give either `access_to_all` or `zone_ids`.** A request with neither reports a missing parameter named `has_at_least_one_parameter`, which is the internal name of the validation group rather than a parameter you can send; it carries a message naming the two real choices.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserZonesBind", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 }, "access_to_all": { "type": "boolean", "description": "When `true`, the sub-user gets access to every geofence of the master account. Use this or `zone_ids`." }, "zone_ids": { "type": "array", "description": "IDs of the geofences to associate with the sub-user. All must belong to the current master account. Use this or `access_to_all`.", "items": { "type": "integer" } } }, "required": [ "subuser_id" ] } } } }, "responses": { "200": { "description": "Geofences bound", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/zones/unbind": { "post": { "tags": [ "Sub-users" ], "summary": "Unbind geofences from sub-user", "description": "Take away a sub-user's access to geofences.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserZonesUnbind", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 }, "zone_ids": { "type": "array", "description": "IDs of the geofences to remove from the sub-user. All must belong to the current master account.", "items": { "type": "integer" } } }, "required": [ "subuser_id", "zone_ids" ] } } } }, "responses": { "200": { "description": "Geofences unbound", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/zones/list_ids": { "post": { "tags": [ "Sub-users" ], "summary": "List sub-user geofence IDs", "description": "Return just the IDs of the geofences a sub-user can see.\n\nUse `subuser/zones/list` when you want the geofence objects themselves.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserZonesListIds", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 } }, "required": [ "subuser_id" ] } } } }, "responses": { "200": { "description": "The geofence IDs in the sub-user's scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "description": "IDs of the geofences available to the sub-user.", "items": { "type": "integer" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/subuser/zones/list": { "post": { "tags": [ "Sub-users" ], "summary": "List sub-user geofences", "description": "Return the geofences a sub-user can see, as full geofence objects, with filtering and pagination.\n\n**Master users only**, and the account needs the `multilevel_access` plan feature on **all** trackers. A sub-user calling this gets error 13, and an account without the feature gets error 236.\n\nRequires the `admin` right.", "operationId": "subuserZonesList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subuser_id": { "type": "integer", "description": "ID of a sub-user belonging to the current account.", "minimum": 1 }, "filter": { "type": "string", "description": "Optional. Filter over geofence label." }, "tag_ids": { "type": "array", "description": "Optional. Tag IDs. A geofence must carry **all** the tags in the list to be returned.", "items": { "type": "integer" } }, "order": { "type": "string", "description": "Optional. Default `id`. How to order the list.", "enum": [ "id", "label" ] }, "limit": { "type": "integer", "description": "Optional. Maximum number of geofences to return." }, "offset": { "type": "integer", "description": "Optional. Default 0. Offset into the results, for pagination.", "minimum": 0 } }, "required": [ "subuser_id" ] } } } }, "responses": { "200": { "description": "The geofences in the sub-user's scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "description": "The geofences available to the sub-user. See the geofence documentation for the object shape.", "items": { "type": "object", "additionalProperties": true } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the account lacks the `multilevel_access` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller is a sub-user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/plugin/list": { "post": { "tags": [ "Account and platform" ], "summary": "List plugins", "description": "List the plugins available to the current user.\n\nPlugins are software modules that change how various API calls behave. Which ones are available varies by user, depending on platform settings and purchased features, and only these can be used to register trackers, generate reports and so on.\n\nCallable with an empty body. Also accepts GET.", "operationId": "pluginList", "responses": { "200": { "description": "The available plugins", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Plugin" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/api/key/create": { "post": { "tags": [ "Account and platform" ], "summary": "Create API key", "description": "Create an API key, and return it.\n\nAn API key is the same thing as a session hash but with unlimited lifetime. It survives a logout and a password change, needs no renewal, does not require storing a login and password, can be revoked on its own, and has a separate rate-limit counter from ordinary user requests.\n\n**Up to 20 keys per account.** Give each one a meaningful title so they can be told apart.\n\n**A standard session hash is required, and an API key is rejected** with error 4, \"User or API key not found or session ended\", which is misleading because the key itself is valid. Get a session hash from `user/auth`. **Master users only**, since the operation needs the `admin` right.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "apiKeyCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "title": { "type": "string", "description": "Title for the new key. Not empty, printable characters only, up to 255 characters.", "minLength": 1, "maxLength": 255 } }, "required": [ "title" ] } } } }, "responses": { "200": { "description": "The new API key", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/ApiKey" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 268: the account already has the maximum of 20 API keys.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 4: the session is invalid, or a non-standard session such as another API key was used. Error 13: the caller is not a master user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/api/key/delete": { "post": { "tags": [ "Account and platform" ], "summary": "Delete API key", "description": "Revoke an API key. Anything using it stops working immediately.\n\n**A standard session hash is required, and an API key is rejected** with error 4, \"User or API key not found or session ended\", which is misleading because the key itself is valid. Get a session hash from `user/auth`. **Master users only**, since the operation needs the `admin` right.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "apiKeyDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "key": { "type": "string", "description": "The API key to delete. Not empty.", "minLength": 1 } }, "required": [ "key" ] } } } }, "responses": { "200": { "description": "API key deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 4: the session is invalid, or a non-standard session such as another API key was used. Error 13: the caller is not a master user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/api/key/list": { "post": { "tags": [ "Account and platform" ], "summary": "List API keys", "description": "List the API keys of the current account.\n\n**A standard session hash is required, and an API key is rejected** with error 4, \"User or API key not found or session ended\", which is misleading because the key itself is valid. Get a session hash from `user/auth`. **Master users only**, since the operation needs the `admin` right.\n\nCallable with an empty body. Also accepts GET.", "operationId": "apiKeyList", "responses": { "200": { "description": "The API keys", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/ApiKey" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 4: the session is invalid, or a non-standard session such as another API key was used. Error 13: the caller is not a master user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/base/nothing": { "post": { "tags": [ "Account and platform" ], "summary": "Health check", "description": "Do nothing and report success. Intended as a health check and as the cheapest way to confirm that a credential still works.\n\nCallable with an empty body. Also accepts GET.", "operationId": "baseNothing", "responses": { "200": { "description": "The platform is reachable and the credential is valid", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/dealer/get_ui_config": { "post": { "tags": [ "Account and platform" ], "summary": "Get dealer UI config", "description": "Return the branding and interface configuration of a dealer: maps, default map position, currency, logos, wallpapers, locale, and the demo account credentials.\n\n**No authentication is required.** This is how a monitoring interface finds out how to present itself before anyone has logged in. Identify the dealer either by `domain`, or by `hash` when a user session already exists.", "operationId": "dealerGetUiConfig", "security": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "domain": { "type": "string", "description": "The dealer's monitoring interface domain, such as `panel.navixy.com`." }, "hash": { "type": "string", "description": "A user session hash, used instead of `domain` to identify the dealer when a session exists." } } } } } }, "responses": { "200": { "description": "The dealer configuration", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "dealer": { "type": "object", "description": "Identity of the dealer.", "properties": { "id": { "type": "integer", "description": "Dealer ID." }, "ui_domain": { "type": "string", "description": "Domain of the dealer monitoring interface." }, "company_url": { "type": "string", "description": "The company website." } } }, "settings": { "$ref": "#/components/schemas/DealerUiSettings" }, "demo_ends": { "type": [ "string", "null" ], "description": "When the demo for this dealer ends. Null when the dealer is not on a trial plan." }, "allow_branding": { "type": "boolean", "description": "True when the dealer may replace the platform branding on the web interface." }, "premium_gis": { "type": "boolean", "description": "True when the dealer has the Premium GIS package." }, "features": { "type": "array", "description": "Features allowed for this dealer. See the dealer features table on the page.", "items": { "type": "string", "enum": [ "branding_web", "branding_mobile", "subpaas", "navixy_label" ] } }, "platform": { "type": "object", "description": "Global platform limits and capabilities. Several other operations refer to these.", "properties": { "iso_datetime_support": { "type": "boolean", "description": "True when the platform supports the ISO 8601 date and time format." }, "history.max_limit": { "type": "integer", "description": "Maximum limit for the history list operations, and their default." }, "report.max_time_span": { "type": "string", "description": "Maximum time span for report generation, as an ISO 8601 duration." }, "stats.max_allowed_trackers": { "type": "integer", "description": "Maximum number of trackers for the stats operations." }, "stats.max_time_span": { "type": "string", "description": "Maximum time span for the stats operations, as an ISO 8601 duration." }, "file_storage.hard_max_file_size": { "type": "integer", "description": "Hard maximum file size in bytes for uploads to file storage.", "format": "int64" }, "form.max_fields_count": { "type": "integer", "description": "Maximum number of fields per form." }, "form.file_field.max_file_size": { "type": "integer", "description": "Maximum size in bytes of a file attached to a form field.", "format": "int64" }, "form.file_field.max_files_per_field": { "type": "integer", "description": "Maximum number of files per form field." }, "form.file_field.max_count": { "type": "integer", "description": "Maximum number of file fields per form." } }, "additionalProperties": true }, "redirect_to": { "type": "string", "description": "Where the caller should be sent instead, when the dealer is configured to redirect. Absent when there is no redirect." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 12: no dealer was found for the given domain or hash. Error 201: not found in the database.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/feedback/send_email": { "post": { "tags": [ "Account and platform" ], "summary": "Send feedback", "description": "Send a message from the user to platform support by email.\n\nA screenshot and a log can be attached, and are delivered as email attachments.", "operationId": "feedbackSendEmail", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "feedback": { "type": "object", "description": "The message from the user. A screenshot and a log attached here are added to the email as attachments.", "additionalProperties": true }, "type": { "type": "string", "description": "Optional. Default `support_request`. What kind of message this is.", "enum": [ "support_request", "feature_request", "review" ] } }, "required": [ "feedback" ] } } } }, "responses": { "200": { "description": "Feedback sent", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/file/stats/read": { "post": { "tags": [ "Account and platform" ], "summary": "Read file storage stats", "description": "Return how much of the account's file storage quota is in use.\n\nUseful before reserving a file with any of the `*/file/create` operations, since those fail with error 268 once the quota is exhausted.\n\nCallable with an empty body. Also accepts GET.", "operationId": "fileStatsRead", "responses": { "200": { "description": "The storage statistics", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "object", "description": "File storage usage for the account.", "properties": { "file_count": { "type": "integer", "description": "Number of stored files." }, "total_size": { "type": "integer", "description": "Total size of stored files, in bytes." }, "quota": { "type": "integer", "description": "Storage quota in bytes." } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/notification/list": { "post": { "tags": [ "Account and platform" ], "summary": "List notifications", "description": "List the platform notifications shown to the current user, such as service announcements.\n\nThese are platform-level messages, not the tracker event history, which lives under `history/`.\n\nCallable with an empty body. Also accepts GET.", "operationId": "notificationList", "responses": { "200": { "description": "The notifications", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "type": "object", "description": "A platform notification shown to the user.", "properties": { "id": { "type": "integer", "description": "ID of the notification." }, "message": { "type": "string", "description": "Text of the notification." }, "show_till": { "type": "string", "description": "Date until which the notification should be shown." } } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/timezone/list": { "post": { "tags": [ "Account and platform" ], "summary": "List timezones", "description": "Return every timezone the platform supports, with descriptions in the requested locale.\n\n**No authentication is required**, and no session is accepted either: this is registration-time information.", "operationId": "timezoneList", "security": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "locale": { "type": "string", "description": "Locale to return the descriptions in, such as `en_US`." } }, "required": [ "locale" ] } } } }, "responses": { "200": { "description": "The supported timezones", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "type": "object", "properties": { "zone_id": { "type": "string", "description": "Timezone ID, used throughout the API. For example `Australia/Sydney`." }, "description": { "type": "string", "description": "Localized description of the timezone." }, "base_offset": { "type": "number", "description": "Base offset from UTC in hours. **May be negative or fractional**: 10 means UTC+10." }, "dst_offset": { "type": "integer", "description": "Daylight saving offset in hours, or 0 when the timezone has no DST rules." }, "country_code": { "type": "string", "description": "ISO country code for the timezone." }, "alt_ids": { "type": "array", "description": "Optional. Alternative identifiers for the same timezone.", "items": { "type": "string" } } }, "additionalProperties": true } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/data/spreadsheet/parse": { "post": { "tags": [ "Account and platform" ], "summary": "Parse spreadsheet", "description": "Upload a spreadsheet, convert it to a tab-separated file on the server, and return its name plus a preview of the parsed rows.\n\n**This is the first step of every import.** The returned name is what `place/import/start`, `employee/import/start` and the other import operations take as `filename`.\n\n**This is a multipart request**, not JSON: `multipart/form-data` with the spreadsheet in a part named `file`. A JSON body returns error 5, \"Wrong request format\".", "operationId": "dataSpreadsheetParse", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "file": { "type": "string", "description": "The spreadsheet to upload, in XLSX, XLS or CSV format.", "format": "binary" }, "preview_count": { "type": "integer", "description": "How many parsed rows to return as a preview. From 1 to 20.", "minimum": 1, "maximum": 20 }, "parse_header": { "type": "boolean", "description": "Whether to read the first row as a header rather than as data." }, "header_map": { "type": "string", "description": "When `parse_header` is `true`, a JSON map of column name to field identifier, such as `{\"Label\": \"label\", \"Latitude\": \"lat\"}`." } }, "required": [ "file" ] } } } }, "responses": { "200": { "description": "The parsed spreadsheet", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "file_id": { "type": "string", "description": "Name of the server-side TSV file, to pass to an import operation as `filename`." }, "preview": { "type": "array", "description": "The first rows as parsed, so the caller can confirm the column mapping before importing.", "items": { "type": "object", "additionalProperties": true } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 234: the file could not be parsed as a spreadsheet.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/data/import/list": { "post": { "tags": [ "Account and platform" ], "summary": "List imports", "description": "List the current user's unfinished import processes **across all entity types at once**.\n\nThis is the cross-family view. Each family also has its own list, such as `place/import/list`, which returns only that family's processes.\n\nCallable with an empty body.", "operationId": "dataImportList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "types": { "type": "array", "description": "Optional. Restrict to these entity types.", "items": { "type": "string", "enum": [ "place", "vehicle", "employee", "driver", "fsm_employee", "zone", "customer" ] } } } } } } }, "responses": { "200": { "description": "Unfinished import processes", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/ImportProcess" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/data/import/read": { "post": { "tags": [ "Account and platform" ], "summary": "Read import", "description": "Return one import process, given both its ID and its entity type.\n\n**`type` is required as well as `process_id`**, because process IDs are only unique within a type. The per-family reads such as `place/import/read` know their own type and take the ID alone.", "operationId": "dataImportRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "process_id": { "type": "integer", "description": "ID of the import process.", "minimum": 1 }, "type": { "type": "string", "description": "Which kind of entity the import is for.", "enum": [ "place", "vehicle", "employee", "driver", "fsm_employee", "zone", "customer" ] } }, "required": [ "process_id", "type" ] } } } }, "responses": { "200": { "description": "The import process", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/ImportProcess" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/history/tracker/list": { "post": { "tags": [ "History" ], "summary": "List tracker history", "description": "Return the event history of one or more trackers over a period.\n\nA request with an empty body reports `interval` as the missing parameter. That is the internal name of the field holding both dates; the public parameters are `from` and `to`.", "operationId": "historyTrackerList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "IDs of the trackers to return history for. All must belong to the authorized user and must not be blocked.", "items": { "type": "integer" } }, "from": { "type": "string", "description": "Start of the period. Include events recorded at or after this date.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period. Include events recorded at or before this date. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "events": { "type": "array", "description": "Optional. Restrict to these event types. Available types come from `history/type/list`.", "items": { "type": "string" } }, "limit": { "type": "integer", "description": "Optional. Defaults to the platform `history.max_limit`, which `dealer/get_ui_config` reports. Maximum number of records to return." }, "ascending": { "type": "boolean", "description": "Optional. Default `true`. Order by time ascending, or descending when `false`." }, "only_emergency": { "type": "boolean", "description": "Optional. Default `false`. Include only emergency events." }, "only_unread": { "type": "boolean", "description": "Optional. Default `false`. Include only unread events." }, "add_tracker_label": { "type": "boolean", "description": "Optional. Default `true`. Add the tracker label to the `message` field." }, "add_tracker_files": { "type": "boolean", "description": "Optional. Default `false`. Include tracker file information. Honoured only for a caller holding the `video_monitoring` right, and ignored otherwise." } }, "required": [ "from", "to" ] } } } }, "responses": { "200": { "description": "Matching history entries", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/HistoryEntry" } }, "limit_exceeded": { "type": "boolean", "description": "`true` when the result was cut short by the platform limit, so more entries match than were returned. There is no total count: use the matching `count` operation for that." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 211: the requested time span is too big. Error 212: the requested period is not available on the current plan. Error 217: one of the given trackers does not exist, is blocked, or lacks the required plan features.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/history/asset/list": { "post": { "tags": [ "History" ], "summary": "List asset history", "description": "Return the event history of assets over a period.\n\n**Only one label is ever prefixed to `message`, as `label: message`.** With `add_asset_label` on, an entry naming a vehicle or an employee takes the asset label, which is the vehicle label or, when there is no vehicle, the employee's full name. An entry naming neither falls back to the tracker label, even with `add_tracker_label` off. With `add_asset_label` off, `add_tracker_label` decides on its own.\n\nA request with an empty body reports `interval` as the missing parameter. That is the internal name of the field holding both dates; the public parameters are `from` and `to`.", "operationId": "historyAssetList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "assets": { "type": "array", "description": "Optional. Default all. The assets to return history for, each an object naming an identifier and a type.", "items": { "type": "object", "properties": { "id": { "type": "integer", "description": "Identifier of the asset." }, "type": { "type": "string", "description": "What kind of asset it is.", "enum": [ "vehicle", "employee" ] } } } }, "asset_types": { "type": "array", "description": "Optional. Default all. Restrict to these asset types.", "items": { "type": "string", "enum": [ "vehicle", "employee" ] } }, "from": { "type": "string", "description": "Start of the period. Include events recorded at or after this date.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period. Include events recorded at or before this date. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "events": { "type": "array", "description": "Optional. Restrict to these event types. Available types come from `history/type/list`.", "items": { "type": "string" } }, "limit": { "type": "integer", "description": "Optional. Defaults to the platform `history.max_limit`, which `dealer/get_ui_config` reports. Maximum number of records to return." }, "ascending": { "type": "boolean", "description": "Optional. Default `true`. Order by time ascending, or descending when `false`." }, "only_emergency": { "type": "boolean", "description": "Optional. Default `false`. Include only emergency events." }, "only_unread": { "type": "boolean", "description": "Optional. Default `false`. Include only unread events." }, "add_asset_label": { "type": "boolean", "description": "Optional. Default `true`. Add the asset label to the `message` field." }, "add_tracker_label": { "type": "boolean", "description": "Optional. Default `false`. Add the tracker label to the `message` field." }, "add_tracker_files": { "type": "boolean", "description": "Optional. Default `false`. Include tracker file information. Honoured only for a caller holding the `video_monitoring` right, and ignored otherwise." } }, "required": [ "from", "to" ] } } } }, "responses": { "200": { "description": "Matching history entries", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/HistoryEntry" } }, "limit_exceeded": { "type": "boolean", "description": "`true` when the result was cut short by the platform limit, so more entries match than were returned. There is no total count: use the matching `count` operation for that." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 211: the requested time span is too big. Error 212: the requested period is not available on the current plan.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/history/asset/count": { "post": { "tags": [ "History" ], "summary": "Count asset history", "description": "Return how many asset history entries match, without returning them.\n\nUse this to size a query before paging through it.\n\nA request with an empty body reports `interval` as the missing parameter. That is the internal name of the field holding both dates; the public parameters are `from` and `to`.", "operationId": "historyAssetCount", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "assets": { "type": "array", "description": "Optional. Default all. The assets to count history for, each an object naming an identifier and a type.", "items": { "type": "object", "properties": { "id": { "type": "integer", "description": "Identifier of the asset." }, "type": { "type": "string", "description": "What kind of asset it is.", "enum": [ "vehicle", "employee" ] } } } }, "asset_types": { "type": "array", "description": "Optional. Default all. Restrict to these asset types.", "items": { "type": "string", "enum": [ "vehicle", "employee" ] } }, "from": { "type": "string", "description": "Start of the period.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "events": { "type": "array", "description": "Optional. Restrict to these event types.", "items": { "type": "string" } }, "only_emergency": { "type": "boolean", "description": "Optional. Default `false`. Count only emergency events." }, "only_unread": { "type": "boolean", "description": "Optional. Default `false`. Count only unread events." } }, "required": [ "from", "to" ] } } } }, "responses": { "200": { "description": "The counts", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "total": { "type": "integer", "description": "Number of matching history entries." }, "total_unread": { "type": "integer", "description": "How many of those are unread." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 211: the requested time span is too big.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/history/user/list": { "post": { "tags": [ "History" ], "summary": "List user history", "description": "Return the account's own event history over a period, as opposed to the history of a particular tracker.\n\n**Only one label is ever prefixed to `message`, as `label: message`.** With `add_asset_label` on, an entry naming a vehicle or an employee takes the asset label, which is the vehicle label or, when there is no vehicle, the employee's full name. An entry naming neither falls back to the tracker label, even with `add_tracker_label` off. With `add_asset_label` off, `add_tracker_label` decides on its own.\n\nA request with an empty body reports `interval` as the missing parameter. That is the internal name of the field holding both dates; the public parameters are `from` and `to`.", "operationId": "historyUserList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "from": { "type": "string", "description": "Start of the period. Include events recorded at or after this date.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period. Include events recorded at or before this date. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "events": { "type": "array", "description": "Optional. Restrict to these event types. Available types come from `history/type/list`.", "items": { "type": "string" } }, "limit": { "type": "integer", "description": "Optional. Defaults to the platform `history.max_limit`, which `dealer/get_ui_config` reports. Maximum number of records to return." }, "ascending": { "type": "boolean", "description": "Optional. Default `true`. Order by time ascending, or descending when `false`." }, "only_emergency": { "type": "boolean", "description": "Optional. Default `false`. Include only emergency events." }, "only_unread": { "type": "boolean", "description": "Optional. Default `false`. Include only unread events." }, "add_asset_label": { "type": "boolean", "description": "Optional. Default `false`. Add the asset label to the `message` field." }, "add_tracker_label": { "type": "boolean", "description": "Optional. Default `true`. Add the tracker label to the `message` field." }, "add_tracker_files": { "type": "boolean", "description": "Optional. Default `false`. Include tracker file information. Honoured only for a caller holding the `video_monitoring` right, and ignored otherwise." } }, "required": [ "from", "to" ] } } } }, "responses": { "200": { "description": "Matching history entries", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/HistoryEntry" } }, "limit_exceeded": { "type": "boolean", "description": "`true` when the result was cut short by the platform limit, so more entries match than were returned. There is no total count: use the matching `count` operation for that." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 211: the requested time span is too big. Error 212: the requested period is not available on the current plan.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/history/type/list": { "post": { "tags": [ "History" ], "summary": "List history event types", "description": "Return every event type the history can contain, with a human-readable description of each.\n\nThe `type` values here are what the `events` filter on the history list operations accepts, and what the `event` field of a history entry carries.\n\nCallable with an empty body.", "operationId": "historyTypeList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "locale": { "type": "string", "description": "Locale code setting the language of the descriptions." }, "only_tracker_events": { "type": "boolean", "description": "Optional. Default `true`. Return only tracker-type events." } }, "required": [ "locale" ] } } } }, "responses": { "200": { "description": "The event types", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "description": "Event type identifier, such as `offline` or `crash_alarm`." }, "description": { "type": "string", "description": "Human-readable description, such as \"Crash\"." } } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/history/unread/list": { "post": { "tags": [ "History" ], "summary": "List unread history", "description": "Return the history entries the user has not read yet.\n\nCallable with an empty body. Also accepts GET.", "operationId": "historyUnreadList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "limit": { "type": "integer", "description": "Optional. Both the default and the maximum are the platform `history.max_limit`, which `dealer/get_ui_config` reports." }, "from": { "type": "string", "description": "Optional. Start of the search period. **Defaults to one year ago**, so a caller wanting everything older must say so.", "examples": [ "2026-08-24 09:00:00" ] } } } } } }, "responses": { "200": { "description": "Unread history entries", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/HistoryEntry" } }, "max_limit": { "type": "integer", "description": "The largest `limit` the platform will honour, the same `history.max_limit` the request parameter is capped at." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 212: the requested period is not available on the current plan.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/history/unread/count": { "post": { "tags": [ "History" ], "summary": "Count unread history", "description": "Return how many history entries the user has not read yet.\n\nCallable with an empty body. Also accepts GET.", "operationId": "historyUnreadCount", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "from": { "type": "string", "description": "Optional. Start of the search period. **Defaults to one year ago.**", "examples": [ "2026-08-24 09:00:00" ] } } } } } }, "responses": { "200": { "description": "The count", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "count": { "type": "integer", "description": "Number of unread history entries." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/history/read": { "post": { "tags": [ "History" ], "summary": "Read history entry", "description": "Return one history entry by ID.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "historyRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the history entry.", "minimum": 1, "format": "int64" }, "add_tracker_label": { "type": "boolean", "description": "Optional. Add the tracker label to the `message` field." } }, "required": [ "id" ] } } } }, "responses": { "200": { "description": "The history entry", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/HistoryEntry" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/history/mark_read": { "post": { "tags": [ "History" ], "summary": "Mark history entry read", "description": "Mark one history entry as read, setting its `is_read` to `true`.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "historyMarkRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the history entry to mark read.", "minimum": 1 } }, "required": [ "id" ] } } } }, "responses": { "200": { "description": "Entry marked read", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/history/mark_read_all": { "post": { "tags": [ "History" ], "summary": "Mark all history read", "description": "Mark **every** unread history entry as read.\n\nThis takes no parameters and cannot be undone: there is no operation to mark an entry unread again. An empty request body is a complete, valid call, so it will act on the whole account.\n\nAlso accepts GET.", "operationId": "historyMarkReadAll", "responses": { "200": { "description": "All entries marked read", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tag/create": { "post": { "tags": [ "Tags" ], "summary": "Create tag", "description": "Create a tag, and return its ID.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `tag_update` right.", "operationId": "tagCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tag": { "allOf": [ { "$ref": "#/components/schemas/Tag" } ], "description": "The tag to create, without the `id` field." } }, "required": [ "tag" ] } } } }, "responses": { "200": { "description": "The created tag", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new tag." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tag/update": { "post": { "tags": [ "Tags" ], "summary": "Update tag", "description": "Update a tag. Pass a full tag object including its `id`.\n\n`avatar_file_name` cannot be set here: it is changed through `tag/avatar/upload`.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `tag_update` right.", "operationId": "tagUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tag": { "allOf": [ { "$ref": "#/components/schemas/Tag" } ], "description": "The tag to update, including its `id`." } }, "required": [ "tag" ] } } } }, "responses": { "200": { "description": "Tag updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tag/delete": { "post": { "tags": [ "Tags" ], "summary": "Delete tags", "description": "Delete one tag or several by ID.\n\n**Give either `tag_id` or `tag_ids`.** A request with neither reports a missing parameter named `has_at_least_one_tag_id`, which is the internal name of the validation group rather than a parameter you can send; it carries a message naming the two real choices.\n\nThe two forms differ in more than shape. **`tag_id` reports error 201 when the tag does not exist, and `tag_ids` does not**: bulk deletion is silent about IDs it could not find.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `tag_update` right.", "operationId": "tagDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tag_id": { "type": "integer", "description": "ID of a single tag to delete. Use this or `tag_ids`, not both." }, "tag_ids": { "type": "array", "description": "IDs of tags to delete. Use this or `tag_id`, not both. Missing IDs are ignored rather than reported.", "items": { "type": "integer" } } } } } } }, "responses": { "200": { "description": "Tags deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tag/list": { "post": { "tags": [ "Tags" ], "summary": "List tags", "description": "List the tags of the current user.\n\nCallable with an empty body. Also accepts GET.", "operationId": "tagList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "filter": { "type": "string", "description": "Optional. Filter over the tag name. 3 to 60 characters, or null.", "minLength": 3, "maxLength": 60 } } } } } }, "responses": { "200": { "description": "Matching tags", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Tag" } }, "count": { "type": "integer", "description": "Total number of tags found. Returned live, and not listed in the published response example." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tag/search": { "post": { "tags": [ "Tags" ], "summary": "Search entities by tag", "description": "Find the entities carrying **all** of the given tags.\n\nThe result is grouped by entity type rather than returned as one list, so each group holds objects of that type. Restrict which groups come back with `entity_types`.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "tagSearch", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tag_ids": { "type": "array", "description": "Tags to search for. An entity must carry **all** of them to match. Must not be empty.", "items": { "type": "integer" }, "minItems": 1 }, "entity_types": { "type": "array", "description": "Optional. Restrict the result to these entity types.", "items": { "type": "string", "enum": [ "place", "task", "task_schedule", "employee", "vehicle", "zone", "tracker" ] } } }, "required": [ "tag_ids" ] } } } }, "responses": { "200": { "description": "Entities carrying all the given tags", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "result": { "type": "object", "description": "The matching entities, grouped by type. Each group holds objects of that type, shaped as that resource documents them.", "properties": { "place": { "type": "array", "description": "Matching places.", "items": { "$ref": "#/components/schemas/Place" } }, "task": { "type": "array", "description": "Matching tasks.", "items": { "$ref": "#/components/schemas/Task" } }, "task_schedule": { "type": "array", "description": "Matching task schedules.", "items": { "$ref": "#/components/schemas/TaskScheduleEntry" } }, "employee": { "type": "array", "description": "Matching employees.", "items": { "$ref": "#/components/schemas/Employee" } }, "vehicle": { "type": "array", "description": "Matching vehicles.", "items": { "$ref": "#/components/schemas/Vehicle" } }, "zone": { "type": "array", "description": "Matching geofences. See the geofence documentation for the object shape.", "items": { "type": "object", "additionalProperties": true } }, "tracker": { "type": "array", "description": "Matching trackers. See the tracker documentation for the object shape.", "items": { "type": "object", "additionalProperties": true } } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tag/avatar/assign": { "post": { "tags": [ "Tags" ], "summary": "Assign tag icon", "description": "Assign an icon from the standard icon set to a tag.\n\nSet `icon_id` to null to use an uploaded avatar instead of an icon.\n\nAlso accepts GET with the same parameters as query-string values.\n\nRequires the `tag_update` right.", "operationId": "tagAvatarAssign", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tag_id": { "type": "integer", "description": "ID of the tag.", "minimum": 1 }, "icon_id": { "type": [ "integer", "null" ], "description": "Icon to assign. Null means use the uploaded avatar instead of an icon." } }, "required": [ "tag_id" ] } } } }, "responses": { "200": { "description": "Icon assigned", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tag/avatar/upload": { "post": { "tags": [ "Tags" ], "summary": "Upload tag avatar", "description": "Upload an avatar image for a tag.\n\n**This is a multipart request**, not JSON: `multipart/form-data` with the image in a part named `file`. The file MIME type must be one of `image/jpeg`, `image/pjpeg`, `image/png`, `image/gif`, or `image/webp`.\n\nThe uploaded image becomes available at `//tag/avatars/`, and the returned name is also what `tag/list` reports as `avatar_file_name`.\n\nIf `redirect_target` is passed, the response is a redirect to that URL with `response=` appended.\n\nRequires the `tag_update` right.", "operationId": "tagAvatarUpload", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "tag_id": { "type": "integer", "description": "ID of the tag." }, "file": { "type": "string", "description": "The image file.", "format": "binary" }, "redirect_target": { "type": "string", "description": "Optional. URL to redirect to after the upload." } }, "required": [ "tag_id", "file" ] } } } }, "responses": { "200": { "description": "The stored avatar", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "string", "description": "Avatar file name." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 233: the `file` part is missing. Error 234: the file has an unexpected MIME type. Error 254: the file could not be saved.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/entity/list": { "post": { "tags": [ "Entities" ], "summary": "List entities", "description": "List the entity classes whose presentation and fields can be customized.\n\n**More than one type exists.** A plain account lists both `place` and `task`. Reading or updating one requires the right mapped to that type, so an account without a product's rights can see its entity in this list and get error 13 on `entity/read`.\n\nCallable with an empty body. Also accepts GET.", "operationId": "entityList", "responses": { "200": { "description": "The customizable entities", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/CustomizableEntity" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/entity/read": { "post": { "tags": [ "Entities" ], "summary": "Read entity", "description": "Return one entity together with its custom fields, by ID or by type.\n\n**Exactly one of `id` and `type` must be given.** Neither is accepted and so is both: the errors say \"cannot be null if type is null\" and \"cannot be non-null if type is not null\" respectively, which is unusually self-explaining for this API.\n\nReading an entity requires the right that governs its type, so `type` values belonging to other products return error 13 rather than data.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "entityRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the entity. Use this or `type`, not both." }, "type": { "type": "string", "description": "Type of the entity. Use this or `id`, not both." } } } } } }, "responses": { "200": { "description": "The entity and its fields", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "entity": { "$ref": "#/components/schemas/CustomizableEntity" }, "fields": { "type": "array", "description": "Custom fields associated with this entity.", "items": { "$ref": "#/components/schemas/CustomField" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller lacks the right that governs this entity type.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/entity/update": { "post": { "tags": [ "Entities" ], "summary": "Update entity", "description": "Update the settings of a customizable entity. The entity must carry a valid `id`.\n\n**The layout must account for every field.** `settings.layout.sections` has to contain every built-in and custom field associated with the entity: nothing may be omitted, and nothing may appear twice, even in different sections. Only reordering is allowed.\n\nThe required right depends on the entity type rather than on the operation, so it is not a fixed rights line: `place` needs `places_custom_fields_update`, and other types need the right their own product defines.", "operationId": "entityUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "entity": { "allOf": [ { "$ref": "#/components/schemas/CustomizableEntity" } ], "description": "The entity to update, with a valid `id` and its full settings." } }, "required": [ "entity" ] } } } }, "responses": { "200": { "description": "Entity updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller lacks the right that governs this entity type.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/entity/fields/read": { "post": { "tags": [ "Entities" ], "summary": "Read entity fields", "description": "Return the custom fields associated with an entity.\n\nThe entity ID comes from `entity/list`.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "entityFieldsRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "entity_id": { "type": "integer", "description": "ID of the entity.", "minimum": 1 } }, "required": [ "entity_id" ] } } } }, "responses": { "200": { "description": "The custom fields", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/CustomField" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/entity/fields/update": { "post": { "tags": [ "Entities" ], "summary": "Update entity fields", "description": "Create, update and optionally delete the custom fields of an entity in one call, and return **all** of the entity's fields afterwards, with new IDs filled in.\n\nFour rules govern what may be sent. A field with `id` null is created. A field that already exists keeps its `type`, which cannot be changed. All fields on one entity must have different `label` values. An `id` that does not exist, or belongs to another entity, is an error.\n\n**`delete_missing` is destructive.** When `true`, every existing field absent from `fields` is permanently deleted. It defaults to `false`, which leaves them alone.\n\nThe required right depends on the entity type: `place` needs `places_custom_fields_update`, and other types need the right their own product defines.", "operationId": "entityFieldsUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "entity_id": { "type": "integer", "description": "ID of the entity.", "minimum": 1 }, "fields": { "type": "array", "description": "The fields to create or update. A field with `id` null is created.", "items": { "$ref": "#/components/schemas/CustomField" } }, "delete_missing": { "type": "boolean", "description": "Optional. Default `false`. When `true`, existing fields absent from `fields` are **permanently deleted**." } }, "required": [ "entity_id", "fields" ] } } } }, "responses": { "200": { "description": "All fields of the entity, after the change", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/CustomField" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller lacks the right that governs this entity type.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/report/tracker/generate": { "post": { "tags": [ "Reports" ], "summary": "Generate report", "description": "Queue a report for generation and return its ID. Generation is asynchronous: poll `report/tracker/status`, then fetch the result with `report/tracker/retrieve` or `report/tracker/download`.\n\nWhich report is produced is decided by `plugin.plugin_id`, and most of the `plugin` object is plugin-specific. See the tracker report plugins reference for what each one accepts.\n\nReport by trackers takes `trackers`; a report by employees takes `employees` instead.\n\n**Error 211 carries extra data.** When the period is too long the error body includes `max_time_span`, a human-readable English string such as `120 days` rather than an ISO 8601 duration, so a caller can retry with a period that fits rather than guessing.\n\nRequires the `reports` right.", "operationId": "reportTrackerGenerate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "from": { "type": "string", "description": "Start of the reporting period.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the reporting period. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "title": { "type": "string", "description": "Report title. A default title is used when null." }, "geocoder": { "type": "string", "description": "Which geocoder to use for resolving addresses." }, "trackers": { "type": "array", "description": "Trackers to include, for a report by trackers.", "items": { "type": "integer" } }, "employees": { "type": "array", "description": "Employees to include, for a report by employees.", "items": { "type": "integer" } }, "time_filter": { "$ref": "#/components/schemas/ReportTimeFilter" }, "plugin": { "type": "object", "description": "Which report to generate, and how. `plugin_id` selects the report type and the rest of the object is plugin-specific.", "properties": { "plugin_id": { "type": "integer", "description": "ID of the tracker report plugin that generates the report." }, "show_seconds": { "type": "boolean", "description": "Whether time values in the report include seconds." } }, "additionalProperties": true }, "include_places_in_address": { "type": "boolean", "description": "Optional. Default `true`. Prepend the name of the place a point falls inside to the address in report columns. Set `false` for the geocoded address alone." }, "include_zones_in_address": { "type": "boolean", "description": "Optional. Default `true`. Prepend the name of the geofence a point falls inside to the address in report columns. Set `false` for the geocoded address alone." } }, "required": [ "from", "to", "plugin" ] } } } }, "responses": { "200": { "description": "The queued report", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the report queued for generation. Use it to poll status and to fetch the result." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: one of the trackers is on a plan with reports disabled.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 211: the period from `from` to `to` is longer than the maximum allowed. **The response body carries `max_time_span`**, a human-readable English string such as `120 days`, not an ISO 8601 duration. Error 217: one or more tracker IDs do not exist or belong to another user. Error 222: the report plugin was not found.", "$ref": "#/components/responses/ResponseError" }, "429": { "description": "Error 15: too many requests. The number of reports one user may generate in parallel is limited.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/report/tracker/status": { "post": { "tags": [ "Reports" ], "summary": "Get report status", "description": "Return how far along a report's generation is.\n\nRequires the `reports` right.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "reportTrackerStatus", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "report_id": { "type": "integer", "description": "ID of the report.", "minimum": 1 } }, "required": [ "report_id" ] } } } }, "responses": { "200": { "description": "The generation progress", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "percent_ready": { "type": "integer", "description": "Report readiness, as a percentage." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: no report with this ID was found.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/report/tracker/retrieve": { "post": { "tags": [ "Reports" ], "summary": "Retrieve report", "description": "Return a generated report as JSON.\n\nThe body of `report` is **plugin-dependent**: its shape follows whichever report plugin generated it.\n\nA report that is still generating returns error 229 rather than partial data, so poll `report/tracker/status` first.\n\nRequires the `reports` right.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "reportTrackerRetrieve", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "report_id": { "type": "integer", "description": "ID of the report.", "minimum": 1 } }, "required": [ "report_id" ] } } } }, "responses": { "200": { "description": "The generated report", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "report": { "$ref": "#/components/schemas/ReportBody" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: no report with this ID was found. Error 229: the report exists but is still being generated.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/report/tracker/download": { "post": { "tags": [ "Reports" ], "summary": "Download report", "description": "Return a generated report as a file.\n\nThe response is the file itself, not a JSON envelope. A report that is still generating returns error 229.\n\nRequires the `reports` right.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "reportTrackerDownload", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "report_id": { "type": "integer", "description": "ID of the report.", "minimum": 1 }, "format": { "type": "string", "description": "Format to render the report in.", "enum": [ "xls", "xlsx", "pdf" ] }, "headless": { "type": "boolean", "description": "Optional. Default `false`. When `true`, omit the title page and the table of contents." } }, "required": [ "report_id", "format" ] } } } }, "responses": { "200": { "description": "The rendered report file", "content": { "application/pdf": { "schema": { "type": "string", "format": "binary" } }, "application/vnd.ms-excel": { "schema": { "type": "string", "format": "binary" } }, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { "schema": { "type": "string", "format": "binary" } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: no report with this ID was found. Error 229: the report exists but is still being generated.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/report/tracker/list": { "post": { "tags": [ "Reports" ], "summary": "List reports", "description": "List every generated and in-progress report belonging to the user, with the parameters each was generated from.\n\n`percent` tells you which are finished without polling each one.\n\nRequires the `reports` right.\n\nCallable with an empty body. Also accepts GET.", "operationId": "reportTrackerList", "responses": { "200": { "description": "The reports", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/ReportDescriptor" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/report/tracker/delete": { "post": { "tags": [ "Reports" ], "summary": "Delete report", "description": "Delete a generated report.\n\nRequires the `reports` right.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "reportTrackerDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "report_id": { "type": "integer", "description": "ID of the report to delete.", "minimum": 1 } }, "required": [ "report_id" ] } } } }, "responses": { "200": { "description": "Report deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 101: the current user has the demo flag, and this operation is disabled in demo mode.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/report/schedule/create": { "post": { "tags": [ "Reports" ], "summary": "Create report schedule", "description": "Create a schedule that generates a report regularly and emails it, and return its ID.\n\nThe `parameters.report` block takes the same fields as `report/tracker/generate`, minus the period, which comes from `parameters.period` instead.\n\nRequires the `reports` right.", "operationId": "reportScheduleCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "schedule": { "allOf": [ { "$ref": "#/components/schemas/ReportScheduleEntry" } ], "description": "The schedule to create, without `id`, `fire_time` or `last_result`." } }, "required": [ "schedule" ] } } } }, "responses": { "200": { "description": "The created schedule", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new schedule entry." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: one of the trackers is on a plan with reports disabled.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 217: one or more tracker IDs do not exist or belong to another user. Error 222: the report plugin was not found.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/report/schedule/update": { "post": { "tags": [ "Reports" ], "summary": "Update report schedule", "description": "Update a report schedule. Pass a full schedule object including its `id`.\n\n`fire_time` and `last_result` are ignored: both are set by the platform.\n\nRequires the `reports` right.", "operationId": "reportScheduleUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "schedule": { "allOf": [ { "$ref": "#/components/schemas/ReportScheduleEntry" } ], "description": "The schedule to update, including its `id`, and without `fire_time` or `last_result`." } }, "required": [ "schedule" ] } } } }, "responses": { "200": { "description": "Schedule updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: one of the trackers is on a plan with reports disabled.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 217: one or more tracker IDs do not exist or belong to another user. Error 222: the report plugin was not found.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/report/schedule/list": { "post": { "tags": [ "Reports" ], "summary": "List report schedules", "description": "List the report schedules of the current user.\n\nRequires the `reports` right.\n\nCallable with an empty body. Also accepts GET.", "operationId": "reportScheduleList", "responses": { "200": { "description": "The schedules", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/ReportScheduleEntry" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/report/schedule/delete": { "post": { "tags": [ "Reports" ], "summary": "Delete report schedule", "description": "Delete a report schedule. Reports it has already generated are left alone.\n\nRequires the `reports` right.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "reportScheduleDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "schedule_id": { "type": "integer", "description": "ID of the report schedule to delete.", "minimum": 1 } }, "required": [ "schedule_id" ] } } } }, "responses": { "200": { "description": "Schedule deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/list": { "post": { "tags": [ "Geofences" ], "summary": "List geofences", "description": "List the geofences of the current user, with filtering and pagination.\n\nPoints are left out unless `with_points` is set, because a polygon or sausage can carry hundreds of them.\n\nCallable with an empty body. Also accepts GET with the same parameters as query-string values.", "operationId": "zoneList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "filter": { "type": "string", "description": "Optional. Filter over geofence label and description." }, "tag_ids": { "type": "array", "description": "Optional. Tag IDs. A geofence must carry **all** the tags in the list to be returned.", "items": { "type": "integer" } }, "offset": { "type": "integer", "description": "Optional. Offset into the found geofences, for pagination.", "minimum": 0 }, "limit": { "type": "integer", "description": "Optional. Number of geofences to return." }, "with_points": { "type": "boolean", "description": "Optional. Default `false`. Include each geofence's points." } } } } } }, "responses": { "200": { "description": "Matching geofences", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Zone" } }, "count": { "type": "integer", "description": "Total number of geofences matching the filter, ignoring pagination. `ZoneListAction` returns a `ListWithCountResponse`, so this is always present." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/read": { "post": { "tags": [ "Geofences" ], "summary": "Read geofence", "description": "Return one geofence by ID.\n\nPoints are left out unless `with_points` is set.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "zoneRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "zone_id": { "type": "integer", "description": "ID of the geofence.", "minimum": 1 }, "with_points": { "type": "boolean", "description": "Optional. Default `false`. Include the geofence's points." } }, "required": [ "zone_id" ] } } } }, "responses": { "200": { "description": "The geofence", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Zone" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/create": { "post": { "tags": [ "Geofences" ], "summary": "Create geofence", "description": "Create a geofence, and return its ID.\n\nPass the zone object without `id`. `color` is optional and defaults to `27A9E3`.\n\nPoints go in the separate `points` parameter rather than inside the zone object, and **must be omitted entirely for a circle**, which cannot have any. A polygon needs at least 3 points and allows at most 500. A sausage needs at least 2 and allows at most 1024. A circle takes no points at all.\n\nRequires the `zone_update` right.", "operationId": "zoneCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "zone": { "allOf": [ { "$ref": "#/components/schemas/Zone" } ], "description": "The geofence to create, without `id`." }, "points": { "type": "array", "description": "Points for a polygon or sausage. **Omit for a circle.** A polygon needs at least 3 points and allows at most 500. A sausage needs at least 2 and allows at most 1024. A circle takes no points at all.", "items": { "$ref": "#/components/schemas/ZonePoint" } } }, "required": [ "zone" ] } } } }, "responses": { "200": { "description": "The created geofence", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new geofence." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 268: the user's quota for geofences is exhausted.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 202: too many points, above 500 for a polygon or 1024 for a sausage. Error 230: points were given for a geofence type that cannot have them, such as a circle. Error 284: not enough points, below 3 for a polygon or 2 for a sausage.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/update": { "post": { "tags": [ "Geofences" ], "summary": "Update geofence", "description": "Update a geofence. Pass the zone object with its `id`.\n\n**The type cannot be changed.** A geofence stored as a circle cannot be submitted as a polygon, and trying returns error 231.\n\nFor a polygon or sausage, including a `points` array inside the zone object updates the points. `zone/point/update` does the same thing on its own.\n\nRequires the `zone_update` right.", "operationId": "zoneUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "zone": { "allOf": [ { "$ref": "#/components/schemas/Zone" } ], "description": "The geofence to update, including its `id`. Its `type` must match what is stored." } }, "required": [ "zone" ] } } } }, "responses": { "200": { "description": "Geofence updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 231: the submitted type differs from the stored type.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/delete": { "post": { "tags": [ "Geofences" ], "summary": "Delete geofences", "description": "Delete one geofence or several by ID.\n\n**Give either `zone_id` or `zone_ids`.** A request with neither reports the message \"There must be one of the parameters - zone_id or zone_ids\" against `zone_ids`.\n\n**Error 203 carries data.** A geofence still referenced by rules cannot be deleted, and the response body includes an `entities` array naming the type and the IDs that reference it, so a caller can go and clear them.\n\nRequires the `zone_update` right.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "zoneDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "zone_id": { "type": "integer", "description": "ID of a single geofence to delete. Use this or `zone_ids`, not both." }, "zone_ids": { "type": "array", "description": "IDs of geofences to delete. Use this or `zone_id`, not both.", "items": { "type": "integer" } } } } } } }, "responses": { "200": { "description": "Geofences deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 203: the geofence is still associated with something, normally a rule. **The response body carries an `entities` array** naming the referencing type and IDs.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/search_location": { "post": { "tags": [ "Geofences" ], "summary": "Search geofences by location", "description": "Return the ID and name of every geofence whose area contains the given coordinates.\n\nThe place equivalent is `place/search_location`.", "operationId": "zoneSearchLocation", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "location": { "type": "object", "description": "The point to test.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } } }, "required": [ "location" ] } } } }, "responses": { "200": { "description": "Geofences containing the point", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of a geofence containing the searched location." }, "label": { "type": "string", "description": "Name of that geofence." } } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/batch_convert": { "post": { "tags": [ "Geofences" ], "summary": "Convert geofence batch", "description": "Parse a tab-delimited batch of **circle** geofences and return them checked, with per-row errors. **Creates nothing.**\n\nGive either `batch` or `file_id`. **If `file_id` is set, `batch` is ignored.** A request with neither returns error 7 with the message \"The 'batch' or 'file_id' parameter must be specified\".\n\nWithin a row, `address` is required when coordinates are absent, and `lat` and `lng` are required when the address is absent.\n\nRequires the `zone_update` right.", "operationId": "zoneBatchConvert", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "batch": { "type": "string", "description": "Batch of tab-delimited geofences." }, "file_id": { "type": "string", "description": "ID of a file preloaded with `data/spreadsheet/parse`. Takes precedence over `batch`." }, "fields": { "type": "array", "description": "Optional. Column names, in order. Defaults to `[\"label\", \"address\", \"lat\", \"lng\", \"radius\", \"tags\"]`.", "items": { "type": "string" } }, "geocoder": { "type": "string", "description": "Optional. Geocoder type to use for resolving addresses." }, "default_radius": { "type": "integer", "description": "Optional. Default 100. Radius in metres for a row that gives none." } } } } } }, "responses": { "200": { "description": "The checked batch", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/CheckedZone" } }, "limit_exceeded": { "type": "boolean", "description": "`true` if the given batch was constrained by a limit." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 234: the batch or file could not be parsed.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/upload": { "post": { "tags": [ "Geofences" ], "summary": "Upload geofences", "description": "Import geofences from a KML file.\n\n**This is a multipart request**, not JSON: `multipart/form-data` with the KML in a part named `file`.\n\n**`dry_run` defaults to `true`**, so a plain call validates and returns the geofences it would create without creating anything. Set it to `false` to create them, which returns their IDs instead.\n\nA `Placemark` with `Point` geometry becomes a circle, taking its radius from the placemark's extended data or from `default_radius`. A `Placemark` with `MultiGeometry` becomes several geofences. Where `Placemark.name` is set it becomes the geofence name, respecting any `Folder` and `Document` hierarchy.\n\nRequires the `zone_update` right.", "operationId": "zoneUpload", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "file": { "type": "string", "description": "The KML file of geofences.", "format": "binary" }, "default_radius": { "type": "integer", "description": "Default radius in metres for a circle or route geofence. Minimum 20, default 150.", "minimum": 20 }, "dry_run": { "type": "boolean", "description": "Default `true`. When `true`, return the geofences that would be created without creating them. When `false`, create them and return their IDs." }, "redirect_target": { "type": "string", "description": "Optional. URL to redirect to after the upload." } }, "required": [ "file" ] } } } }, "responses": { "200": { "description": "The geofences that would be created, or the IDs of those created", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "description": "With `dry_run` true, the geofences ready to create. With `dry_run` false, the IDs of the geofences created.", "items": {} } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 233: the file part is missing. Error 234: the file could not be parsed. Error 268: the user's quota for geofences is exhausted.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 202: too many points, above 500 for a polygon or 1024 for a sausage. Error 284: not enough points, below 3 for a polygon or 2 for a sausage.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/download": { "post": { "tags": [ "Geofences" ], "summary": "Download geofences", "description": "Return geofences as a KML or KMZ file.\n\nThe response is the file itself, not a JSON envelope. Both parameters are optional, so an empty body downloads every available geofence as KML.", "operationId": "zoneDownload", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "format": { "type": "string", "description": "Optional. Default `kml`. File format.", "enum": [ "kml", "kmz" ] }, "zone_ids": { "type": "array", "description": "Optional. Geofences to include. When null, every available geofence is downloaded.", "items": { "type": "integer" } } } } } } }, "responses": { "200": { "description": "The geofences as a KML or KMZ file", "content": { "application/vnd.google-earth.kml+xml": { "schema": { "type": "string", "format": "binary" } }, "application/vnd.google-earth.kmz": { "schema": { "type": "string", "format": "binary" } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/point/list": { "post": { "tags": [ "Geofences" ], "summary": "List geofence points", "description": "Return the points of a geofence.\n\nPoints are handled separately from the geofence itself because they arrive as large arrays. `zone/read` with `with_points` returns the same data alongside the geofence.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "zonePointList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "zone_id": { "type": "integer", "description": "ID of the geofence.", "minimum": 1 }, "count": { "type": "integer", "description": "Optional. Simplify the returned list down to about this many points." } }, "required": [ "zone_id" ] } } } }, "responses": { "200": { "description": "The geofence points", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/ZonePoint" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 230: this geofence type cannot have points, for example a circle.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/point/update": { "post": { "tags": [ "Geofences" ], "summary": "Update geofence points", "description": "Replace the points of a geofence.\n\n**This is a replacement, not a merge**: the array given becomes the geofence's points.\n\nA polygon needs at least 3 points and allows at most 500. A sausage needs at least 2 and allows at most 1024. A circle takes no points at all.\n\nRequires the `zone_update` right.", "operationId": "zonePointUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "zone_id": { "type": "integer", "description": "ID of the geofence.", "minimum": 1 }, "points": { "type": "array", "description": "The geofence's new points. A polygon needs at least 3 points and allows at most 500. A sausage needs at least 2 and allows at most 1024. A circle takes no points at all.", "items": { "$ref": "#/components/schemas/ZonePoint" } } }, "required": [ "zone_id", "points" ] } } } }, "responses": { "200": { "description": "Points updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 202: too many points for this geofence type. Error 284: not enough points, below 3 for a polygon or 2 for a sausage.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 230: this geofence type cannot have points, for example a circle.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/import/start": { "post": { "tags": [ "Geofences" ], "summary": "Start geofence import", "description": "Start the background process that imports **circle** geofences from a preloaded TSV file, and return the process ID. Only circles can be imported.\n\nCreate the file first with `data/spreadsheet/parse`, which converts a spreadsheet to TSV and returns its name as `file_id`. Pass that name here as `filename`.\n\nAddress and coordinates complete each other: given only an address the coordinates come from geocoding, and given only coordinates the address comes from reverse geocoding.\n\nTags that do not exist yet are created automatically, within the user's tag quota.\n\nRequires the `zone_update` right.", "operationId": "zoneImportStart", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "filename": { "type": "string", "description": "Name of the server-side TSV file, as returned by `data/spreadsheet/parse` in its `file_id`. Must not be empty.", "minLength": 1 }, "headers": { "type": "array", "description": "What each column of the file holds, in order. Must not be empty. Accepted names are `label`, `address`, `lat`, `lng`, `radius` which defaults to 100, `tags`, and `undefined` for a column whose meaning is not known.", "items": { "type": "string" }, "minItems": 1 }, "user_headers": { "type": "array", "description": "Optional. Display labels for the columns. Must be the same length as `headers`.", "items": { "type": "string" } } }, "required": [ "filename", "headers" ] } } } }, "responses": { "200": { "description": "The started import", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the created import process." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 233: the preloaded file was not found. Error 234: the file is not a TSV.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 247: an identical import already exists for the same file.", "$ref": "#/components/responses/ResponseError" }, "429": { "description": "Error 15: too many requests, when too many imports are already in progress.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/import/read": { "post": { "tags": [ "Geofences" ], "summary": "Read geofence import", "description": "Return one geofence import process by ID, including its progress.\n\nThe import process object is the same shape every import family returns, differing only in `type`, which is `zone` here.", "operationId": "zoneImportRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "process_id": { "type": "integer", "description": "ID of the import process.", "minimum": 1 } }, "required": [ "process_id" ] } } } }, "responses": { "200": { "description": "The import process", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/ImportProcess" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/import/list": { "post": { "tags": [ "Geofences" ], "summary": "List geofence imports", "description": "List the current user's unfinished geofence import processes.\n\nCallable with an empty body.", "operationId": "zoneImportList", "responses": { "200": { "description": "Unfinished import processes", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/ImportProcess" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/import/download_failed": { "post": { "tags": [ "Geofences" ], "summary": "Download failed geofence rows", "description": "Return a file of the rows that failed validation, so they can be corrected and imported again.\n\nThe response is the file itself, not a JSON envelope.", "operationId": "zoneImportDownloadFailed", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "process_id": { "type": "integer", "description": "ID of the import process.", "minimum": 1 } }, "required": [ "process_id" ] } } } }, "responses": { "200": { "description": "The failed rows as a file", "content": { "text/tab-separated-values": { "schema": { "type": "string", "format": "binary" } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: the file of failed rows was not found.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/zone/import/finish": { "post": { "tags": [ "Geofences" ], "summary": "Finish geofence import", "description": "Mark an import process as finished, which drops it out of `zone/import/list`.\n\n**Only a process that has stopped can be finished.** One still running returns error 280.", "operationId": "zoneImportFinish", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "process_id": { "type": "integer", "description": "ID of the import process.", "minimum": 1 } }, "required": [ "process_id" ] } } } }, "responses": { "200": { "description": "Import marked finished", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/asset/track/list": { "post": { "tags": [ "Assets" ], "summary": "List asset tracks", "description": "Return the tracks of an asset over a period, one entry per tracker the asset was attached to.\n\nEach entry carries its own `interval`, which is the window during which the asset was attached to that tracker, not the window you asked for.\n\nThis is the **asset** track resource, `asset/track/*`, which takes an asset and resolves its trackers for you. It is a different resource from `track/*`, which takes tracker IDs directly.\n\nA request with an empty body reports `interval` as a missing parameter alongside `asset`. That is the internal name of the field holding both dates; the public parameters are `from` and `to`.", "operationId": "assetTrackList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "asset": { "type": "object", "description": "Which asset to report on. The tracker list is derived from the trackers currently related to this asset.", "properties": { "id": { "type": "integer", "description": "ID of the asset." }, "type": { "type": "string", "description": "What kind of asset it is.", "enum": [ "vehicle", "employee" ] } } }, "from": { "type": "string", "description": "Start of the period. The response begins with the next track point after this time.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period. Must be later than `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "filter": { "type": "boolean", "description": "Optional. Default `true`. Drop tracks judged too short by length and point count." }, "split": { "type": "boolean", "description": "Optional. Default `true`. When `false`, all tracks in the period are combined into one, returned as a `merged` track." }, "include_gsm_lbs": { "type": "boolean", "description": "Optional. Default `true`. When `false`, GSM LBS points are excluded." }, "cluster_single_reports": { "type": "boolean", "description": "Optional. Default `false`. When `true`, single-point trips are grouped by coordinates into `cluster` tracks." }, "count_events": { "type": "boolean", "description": "Optional. Default `false`. When `true`, each multi-point track carries `event_count`." }, "omit_addresses": { "type": "boolean", "description": "Optional. Default `false`. When `true`, address fields come back empty." }, "with_points": { "type": "boolean", "description": "Optional. Default `false`. When `true`, each track carries its `points_list`." }, "point_limit": { "type": "integer", "description": "Optional. Reduce the returned points to about this many, from 2 to 3000. Without it the server's own simplification settings apply. **Not a hard limit**: the response may contain more.", "minimum": 2, "maximum": 3000 } }, "required": [ "asset", "from", "to" ] } } } }, "responses": { "200": { "description": "The asset's tracks, per tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "Tracker identifier." }, "limit_exceeded": { "type": "boolean", "description": "`true` when the requested period is longer than the tracker's plan stores, so the data is truncated." }, "interval": { "type": "object", "description": "When the asset was attached to this tracker, which is not necessarily the period requested.", "properties": { "from": { "type": "string", "description": "Start of the attachment interval.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the attachment interval.", "examples": [ "2026-08-24 09:00:00" ] } } }, "tracks": { "type": "array", "items": { "$ref": "#/components/schemas/Track" } } } } }, "total": { "$ref": "#/components/schemas/TrackTotals" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 211: the period between `from` and `to` is longer than the API configuration allows.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 201: the asset in the request does not exist. The description names it, for example \"Employee #1 not found\".", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/asset/track/read": { "post": { "tags": [ "Assets" ], "summary": "Read asset track points", "description": "Return the individual track points recorded for an asset over a period, one entry per tracker the asset was attached to.\n\nEach point's timestamp is when the tracker recorded it, in the user's time zone.\n\nThis is the **asset** track resource, `asset/track/*`, which takes an asset and resolves its trackers for you. It is a different resource from `track/*`, which takes tracker IDs directly.\n\nA request with an empty body reports `interval` as a missing parameter alongside `asset`. That is the internal name of the field holding both dates; the public parameters are `from` and `to`.", "operationId": "assetTrackRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "asset": { "type": "object", "description": "Which asset to report on. The tracker list is derived from the trackers currently related to this asset.", "properties": { "id": { "type": "integer", "description": "ID of the asset." }, "type": { "type": "string", "description": "What kind of asset it is.", "enum": [ "vehicle", "employee" ] } } }, "from": { "type": "string", "description": "Start of the period. The response begins with the next track point after this time.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period. Must be later than `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "track_id": { "type": "integer", "description": "Optional. Return only the points of this track. Without it, every valid track point in the period is returned." }, "include_gsm_lbs": { "type": "boolean", "description": "Optional. Default `true`. When `false` **and no `track_id` is given**, GSM LBS points are excluded." }, "simplify": { "type": "boolean", "description": "Optional. Default `true`. When `true`, the returned data is simplified to fewer points." }, "point_limit": { "type": "integer", "description": "Optional. Only applies when `simplify` is `true`. Reduce the returned points to about this many, from 2 to 3000. **Not a hard limit**: the response may contain more.", "minimum": 2, "maximum": 3000 }, "filter": { "type": "boolean", "description": "Optional. When `true`, tracks are filtered, which currently affects LBS tracks only. When `false`, parking points are included." } }, "required": [ "asset", "from", "to" ] } } } }, "responses": { "200": { "description": "The asset's track points, per tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "Tracker identifier." }, "limit_exceeded": { "type": "boolean", "description": "`true` when the requested period is longer than the tracker's plan stores." }, "interval": { "type": "object", "description": "When the asset was attached to this tracker.", "properties": { "from": { "type": "string", "description": "Start of the attachment interval.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the attachment interval.", "examples": [ "2026-08-24 09:00:00" ] } } }, "track_points": { "type": "array", "items": { "$ref": "#/components/schemas/TrackPoint" } } } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 211: the period between `from` and `to` is longer than the API configuration allows.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 201: the asset in the request does not exist.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/asset_group/create": { "post": { "tags": [ "Assets" ], "summary": "Create asset group", "description": "Create an asset group, and return its ID.\n\n**Only one asset of each type per group.**\n\nWhen an asset already belongs to another group, `force_reassign` decides what happens: `false` refuses with error 290 and lists the conflicts, `true` moves the asset and reports what it removed in `reassigned_assets`.", "operationId": "assetGroupCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "description": "Optional. Name of the group. Defaults to the new entry's own ID." }, "assets": { "type": "array", "description": "Assets to put in the group. Only one asset of each type is allowed.", "items": { "$ref": "#/components/schemas/AssetId" } }, "force_reassign": { "type": "boolean", "description": "Optional. Default `false`. When `true`, assets already in another group are moved, and their previous assignments removed." } }, "required": [ "assets" ] } } } }, "responses": { "200": { "description": "The created group", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the group." }, "reassigned_assets": { "type": "array", "description": "Optional. Assignments removed while reassigning.", "items": { "$ref": "#/components/schemas/AssetGroupObject" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 291: the request contains assets not accessible to the user.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 290: an asset is already in a group and `force_reassign` is `false`. **The response body carries `assigned_assets`**, naming the existing assignments.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/asset_group/list": { "post": { "tags": [ "Assets" ], "summary": "List asset groups", "description": "List asset groups, by group ID or by the asset they contain.\n\n**At most one of `group_ids` and `asset` may be given, and neither is also fine.** An empty body succeeds. Sending both is what fails, reporting the validation group `has_not_both_group_ids_and_asset_fields` with the message \"None or one of the fields must be present\".\n\nCallable with an empty body.", "operationId": "assetGroupList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "group_ids": { "type": "array", "description": "Groups to return. Use this or `asset`, not both.", "items": { "type": "integer" } }, "asset": { "allOf": [ { "$ref": "#/components/schemas/AssetId" } ], "description": "Return the group containing this asset. Use this or `group_ids`, not both." } } } } } }, "responses": { "200": { "description": "Matching asset groups", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/AssetGroup" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/asset_group/set": { "post": { "tags": [ "Assets" ], "summary": "Set assets in group", "description": "Put assets into an existing group.\n\n**Only one asset of each type per group**, and `assets` must not be empty.\n\nWhen an asset already belongs to another group, `force_reassign` decides what happens: `false` refuses with error 290 and lists the conflicts, `true` moves the asset and reports what it removed in `reassigned_assets`.", "operationId": "assetGroupSet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the group.", "minimum": 1 }, "assets": { "type": "array", "description": "Assets to set in the group. Only one asset of each type is allowed, and the list must not be empty.", "items": { "$ref": "#/components/schemas/AssetId" }, "minItems": 1 }, "force_reassign": { "type": "boolean", "description": "Optional. Default `false`. When `true`, assets already in another group are moved, and their previous assignments removed." } }, "required": [ "id", "assets" ] } } } }, "responses": { "200": { "description": "Assets set", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "reassigned_assets": { "type": "array", "description": "Optional. Assignments removed while reassigning.", "items": { "$ref": "#/components/schemas/AssetGroupObject" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 291: the request contains assets not accessible to the user.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 290: an asset is already in a group and `force_reassign` is `false`. **The response body carries `assigned_assets`**, naming the existing assignments.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/asset_group/remove": { "post": { "tags": [ "Assets" ], "summary": "Remove assets from group", "description": "Take assets out of a group.\n\n**Every asset named must already be in the group**, or the call fails with error 286 and removes nothing. `assets` must not be empty.", "operationId": "assetGroupRemove", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the group.", "minimum": 1 }, "assets": { "type": "array", "description": "Assets to remove. Only one asset of each type is allowed, and the list must not be empty.", "items": { "$ref": "#/components/schemas/AssetId" }, "minItems": 1 } }, "required": [ "id", "assets" ] } } } }, "responses": { "200": { "description": "Assets removed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 286: not every named asset is in the group. Error 287: the group contains no assets accessible to the user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/asset_group/update": { "post": { "tags": [ "Assets" ], "summary": "Rename asset group", "description": "Change the name of an asset group. This is the only field it changes: use `asset_group/set` and `asset_group/remove` to alter membership.\n\n**`name` must not be empty**.", "operationId": "assetGroupUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the group.", "minimum": 1 }, "name": { "type": "string", "description": "New name for the group. Must not be empty.", "minLength": 1 } }, "required": [ "id", "name" ] } } } }, "responses": { "200": { "description": "Group renamed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 287: at least one asset in the group must be accessible to the user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/asset_group/delete": { "post": { "tags": [ "Assets" ], "summary": "Delete asset group", "description": "Delete an asset group.\n\n**The group is marked deleted rather than erased**, so its history stays intact.", "operationId": "assetGroupDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the group.", "minimum": 1 } }, "required": [ "id" ] } } } }, "responses": { "200": { "description": "Group deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 289: the group contains assets not accessible to the user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/apn_settings/read": { "post": { "tags": [ "Tracking services" ], "summary": "Read APN settings", "description": "Look up the mobile-internet APN settings for a phone number, so a device on that SIM can be configured to reach the platform.\n\nThe lookup is by number, not by tracker: the number does not have to belong to a registered device. Settings are resolved against the dealer the current account belongs to.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "apnSettingsRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "phone": { "type": "string", "description": "A valid international phone number **without the leading `+`**, digits only, for example `1234567890`." } }, "required": [ "phone" ] } } } }, "responses": { "200": { "description": "The APN settings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/ApnSettings" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/beacon/data/read": { "post": { "tags": [ "Tracking services" ], "summary": "Read BLE beacon data history", "description": "Return BLE beacon scans recorded between `from` and `to`, sorted by `get_time`.\n\nBeacon data is what a tracker saw of the radio tags around it: iBeacon, Teltonika EYE Beacon or Sensor, Eddystone and similar.\n\n**Omitting `trackers` means every tracker available to the caller**, not none. The interval is additionally clipped by each tracker's history-depth limit, and a tracker whose limit excludes the whole interval contributes nothing rather than raising an error.", "operationId": "beaconDataRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "from": { "type": "string", "description": "Start of the period, in `YYYY-MM-DD HH:MM:SS` format.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period, in `YYYY-MM-DD HH:MM:SS` format. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "trackers": { "type": "array", "description": "Optional. Tracker IDs. **Omit to search every tracker available to the caller.** Cannot be an empty array.", "items": { "type": "integer" } }, "beacons": { "type": "array", "description": "Optional. Beacon hardware IDs. Each must be 1 to 64 characters. Omit to return data for all beacons.", "items": { "type": "string", "description": "Beacon hardware ID.", "minLength": 1, "maxLength": 64 } } }, "required": [ "from", "to" ] } } } }, "responses": { "200": { "description": "Matching beacon scans", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/BeaconDataEntry" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: one of the trackers is blocked, normally because its tariff ended. Error 217: one or more of the tracker IDs does not exist or belongs to another user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/beacon/data/last_values": { "post": { "tags": [ "Tracking services" ], "summary": "Read last BLE beacon values", "description": "Return the most recent scan of each BLE beacon currently visible to the given trackers.\n\nCallable with an empty body, which reads every tracker available to the caller over the last hour.", "operationId": "beaconDataLastValues", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Optional. Tracker IDs. **Omit to read every tracker available to the caller.** Cannot be an empty array.", "items": { "type": "integer" } }, "skip_older_than_seconds": { "type": "integer", "description": "Optional. Default 3600. Ignore scans older than this many seconds." } } } } } }, "responses": { "200": { "description": "Last beacon values", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/BeaconDataEntry" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: one of the trackers is blocked, normally because its tariff ended. Error 217: one or more of the tracker IDs does not exist or belongs to another user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/delivery/read": { "post": { "tags": [ "Tracking services" ], "summary": "Read delivery state", "description": "Return enough to track one delivery: the task or checkpoint, the tracker carrying it, and who is driving.\n\nOnly tasks and checkpoints whose start date has already passed and whose status is `assigned` or `arrived` are searched.\n\nBesides an ordinary user session, this call accepts the special **delivery** session type, which is what a recipient-facing delivery tracking page uses.\n\nSeveral records can share an `external_id`. This call returns only one: a task in preference to a checkpoint, and the earliest by start date. Use `delivery/list` to get them all.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "deliveryRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "external_id": { "type": "string", "description": "External ID of the task. 1 to 100 characters, trimmed of surrounding whitespace.", "minLength": 1, "maxLength": 100 } }, "required": [ "external_id" ] } } } }, "responses": { "200": { "description": "The delivery", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "task": { "$ref": "#/components/schemas/Task" }, "tracker": { "type": "object", "description": "The tracker carrying the delivery. A narrower record than the one the tracker calls return: it has no tag bindings.", "properties": { "id": { "type": "integer", "description": "Tracker ID, also known as the object ID.", "examples": [ 123456 ] }, "label": { "type": "string", "description": "Tracker label.", "examples": [ "tracker label" ] }, "clone": { "type": "boolean", "description": "`true` when this tracker is a clone of another." }, "group_id": { "type": "integer", "description": "Tracker group ID, `0` when the tracker is in no group.", "examples": [ 167 ] }, "avatar_file_name": { "type": "string", "description": "Avatar file name. Present only when the tracker has one." }, "source": { "$ref": "#/components/schemas/TrackerSource" } } }, "restrictions": { "$ref": "#/components/schemas/TariffRestrictions" }, "user_id": { "type": "integer", "description": "Master account ID the task belongs to." }, "first_name": { "type": [ "string", "null" ], "description": "First name of the employee assigned to the task, or `null`." }, "middle_name": { "type": [ "string", "null" ], "description": "Middle name of the employee assigned to the task, or `null`." }, "last_name": { "type": [ "string", "null" ], "description": "Last name of the employee assigned to the task, or `null`." }, "vehicle_label": { "type": [ "string", "null" ], "description": "Label of the vehicle assigned to the task, or `null`." }, "estimated_time": { "type": [ "integer", "null" ], "description": "Estimated time of arrival in seconds, or `null` when it could not be calculated. Derived by routing from the tracker's last known location to the task location." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not include the delivery tracking feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/delivery/list": { "post": { "tags": [ "Tracking services" ], "summary": "List delivery states", "description": "Return every task and checkpoint matching an external ID, rather than the single best match `delivery/read` returns.\n\nOnly tasks and checkpoints whose start date has already passed and whose status is `assigned` or `arrived` are searched.\n\nBesides an ordinary user session, this call accepts the special **delivery** session type, which is what a recipient-facing delivery tracking page uses.\n\nReturns error 201 when nothing matches, rather than an empty list.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "deliveryList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "external_id": { "type": "string", "description": "External ID of the task. 1 to 100 characters, trimmed of surrounding whitespace.", "minLength": 1, "maxLength": 100 } }, "required": [ "external_id" ] } } } }, "responses": { "200": { "description": "The deliveries", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "type": "object", "properties": { "task": { "$ref": "#/components/schemas/Task" }, "tracker": { "type": "object", "description": "The tracker carrying the delivery. A narrower record than the one the tracker calls return: it has no tag bindings.", "properties": { "id": { "type": "integer", "description": "Tracker ID, also known as the object ID.", "examples": [ 123456 ] }, "label": { "type": "string", "description": "Tracker label.", "examples": [ "tracker label" ] }, "clone": { "type": "boolean", "description": "`true` when this tracker is a clone of another." }, "group_id": { "type": "integer", "description": "Tracker group ID, `0` when the tracker is in no group.", "examples": [ 167 ] }, "avatar_file_name": { "type": "string", "description": "Avatar file name. Present only when the tracker has one." }, "source": { "$ref": "#/components/schemas/TrackerSource" } } }, "first_name": { "type": [ "string", "null" ], "description": "First name of the employee assigned to the task, or `null`." }, "middle_name": { "type": [ "string", "null" ], "description": "Middle name of the employee assigned to the task, or `null`." }, "last_name": { "type": [ "string", "null" ], "description": "Last name of the employee assigned to the task, or `null`." }, "vehicle_label": { "type": [ "string", "null" ], "description": "Label of the vehicle assigned to the task, or `null`." }, "estimated_time": { "type": [ "integer", "null" ], "description": "Estimated time of arrival in seconds, or `null` when it could not be calculated. Derived by routing from the tracker's last known location to the task location." } } } }, "user_id": { "type": "integer", "description": "Master account ID the tasks belong to." }, "restrictions": { "$ref": "#/components/schemas/TariffRestrictions" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not include the delivery tracking feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/geocoder/search_address": { "post": { "tags": [ "Tracking services" ], "summary": "Search address", "description": "Forward geocoding: return the locations matching an address, most relevant first.\n\n**A coordinate pair in `q` is handled as reverse geocoding instead.** When `q` is two decimal numbers separated by a comma, such as `37.825014,-122.372020`, it is read as latitude and longitude and the address at that point is returned as a single result. Both numbers must carry a decimal point for this to trigger.\n\nReturns error 210 when nothing matches.", "operationId": "geocoderSearchAddress", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "q": { "type": "string", "description": "Address, place name, or a `lat,lng` coordinate pair. Trimmed of surrounding whitespace and cannot be empty.", "minLength": 1 }, "geocoder": { "type": "string", "description": "Optional. Preferred geocoder. Accounts with premium GIS always use Google regardless of this value.", "enum": [ "google", "yandex", "progorod", "osm", "doublegis", "locationiq" ] }, "bounds": { "allOf": [ { "$ref": "#/components/schemas/GeoBounds" } ], "description": "Optional. Box the results should preferably come from. This weights relevance rather than filtering: a strong match outside the box can still be returned." }, "lang": { "type": "string", "description": "Optional. ISO 639 language code for the result, at most 5 characters, for example `en` or `en_US`. Defaults to the session language.", "maxLength": 5 }, "with_details": { "type": "boolean", "description": "Optional. Default `false`. Include the parsed `details` object in the response." } }, "required": [ "q" ] } } } }, "responses": { "200": { "description": "Matching locations", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "locations": { "type": "array", "items": { "$ref": "#/components/schemas/GeocodedLocation" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 210: no location matched the query.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/geocoder/search_location": { "post": { "tags": [ "Tracking services" ], "summary": "Search location", "description": "Reverse geocoding: return the address at a coordinate pair.\n\nThe point may be given either as the `location` object or as separate top-level `lat` and `lng` parameters. `location` wins if both are present.", "operationId": "geocoderSearchLocation", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "location": { "type": "object", "description": "The point to resolve. Alternative to the separate `lat` and `lng` parameters.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } }, "required": [ "lat", "lng" ] }, "lat": { "type": "number", "description": "Latitude. Use with `lng` instead of `location`." }, "lng": { "type": "number", "description": "Longitude. Use with `lat` instead of `location`." }, "geocoder": { "type": "string", "description": "Optional. Preferred geocoder. Accounts with premium GIS always use Google regardless of this value.", "enum": [ "google", "yandex", "progorod", "osm", "doublegis", "locationiq" ] }, "lang": { "type": "string", "description": "Optional. ISO 639 language code for the result, at most 5 characters, for example `en` or `en_US`. Defaults to the session language.", "maxLength": 5 }, "with_details": { "type": "boolean", "description": "Optional. Default `false`. Include the parsed `details` object in the response." }, "goal": { "type": "string", "description": "Optional. Default `ui`. What the lookup is for, which decides the geocoder chosen. Use `ui_user_action` for a lookup a user asked for directly, `ui` otherwise. **An unrecognised value is silently treated as `ui`.**", "enum": [ "ui", "ui_user_action" ] } }, "required": [ "location" ] } } } }, "responses": { "200": { "description": "The address", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "string", "description": "The address at the point." }, "details": { "allOf": [ { "$ref": "#/components/schemas/AddressDetails" } ], "description": "Present only when `with_details` was set." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/map_layer/read": { "post": { "tags": [ "Tracking services" ], "summary": "Read map layer", "description": "Return the body of a map layer.\n\n**The response is the KML file itself**, served as `application/vnd.google-earth.kml+xml; charset=utf-8`, not a JSON envelope. Only a failure comes back as JSON.\n\nReachable from a geo-link session as well as an ordinary one.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "mapLayerRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the map layer.", "minimum": 1 } }, "required": [ "id" ] } } } }, "responses": { "200": { "description": "The layer body as KML", "content": { "application/vnd.google-earth.kml+xml": { "schema": { "type": "string", "description": "The KML document." } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/map_layer/list": { "post": { "tags": [ "Tracking services" ], "summary": "List map layers", "description": "Return the metadata of every map layer belonging to the account. Bodies are not included; read them one at a time with `map_layer/read`.\n\nCallable with an empty body. Reachable from a geo-link session as well as an ordinary one. Also accepts GET.", "operationId": "mapLayerList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": {} } } } }, "responses": { "200": { "description": "The map layers", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/MapLayer" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/map_layer/upload": { "post": { "tags": [ "Tracking services" ], "summary": "Upload map layer", "description": "Create a map layer from a KML file.\n\n**This is a multipart request**, not JSON: `multipart/form-data` with the KML in a part named `file`. The file is validated against the KML 2.0 and 2.2 schemas and rejected if it does not conform.", "operationId": "mapLayerUpload", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "file": { "type": "string", "description": "The KML file to import.", "format": "binary" }, "label": { "type": "string", "description": "Name for the new layer. Trimmed, and cannot be empty.", "minLength": 1 }, "redirect_target": { "type": "string", "description": "Optional. URL to redirect to after the upload. When given, the response is a redirect to `?response=` instead of a JSON body." } }, "required": [ "file", "label" ] } } } }, "responses": { "200": { "description": "The created layer", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new map layer." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 268: the account's quota for map layers is exhausted.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 233: the `file` part is missing. Error 234: the file has the wrong MIME type. Error 242: the file is not valid KML.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/map_layer/update": { "post": { "tags": [ "Tracking services" ], "summary": "Update map layer", "description": "Rename a map layer.\n\nOnly the metadata changes. To replace the KML body, upload a new layer and delete the old one.", "operationId": "mapLayerUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "layer": { "allOf": [ { "$ref": "#/components/schemas/MapLayer" } ], "description": "The layer to update. `id` is required and identifies it." } }, "required": [ "layer" ] } } } }, "responses": { "200": { "description": "Layer updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/map_layer/delete": { "post": { "tags": [ "Tracking services" ], "summary": "Delete map layer", "description": "Delete a map layer and its body.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "mapLayerDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the map layer.", "minimum": 1 } }, "required": [ "id" ] } } } }, "responses": { "200": { "description": "Layer deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/retranslator/create": { "post": { "tags": [ "Tracking services" ], "summary": "Create retranslator", "description": "Create a retranslator, which forwards the data a device sends to the platform on to a third-party server.\n\nPass the retranslator object without `id`.\n\nAddress, port and login must be unique within the account; a duplicate returns error 247. Which of `login` and `password` are required depends on the protocol: read `required_login` and `required_password` from `retranslator/protocols/list` before saving one. A missing required value returns error 7 naming the field.\n\nRequires the `admin` right, so only a master user can call it.", "operationId": "retranslatorCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "retranslator": { "allOf": [ { "$ref": "#/components/schemas/Retranslator" } ], "description": "The retranslator to create, without `id`." } }, "required": [ "retranslator" ] } } } }, "responses": { "200": { "description": "The created retranslator", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new retranslator." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 268: the account's quota for retranslators is exhausted.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 247: a retranslator with this address, port and login already exists. Error 206: the login is already in use, which the Navixy Web Service protocol enforces across the platform.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/retranslator/update": { "post": { "tags": [ "Tracking services" ], "summary": "Update retranslator", "description": "Update a retranslator. It must exist and belong to the current account.\n\n**`id` is required inside the object** and identifies which retranslator to change. Every other field is replaced by what is sent.\n\nAddress, port and login must be unique within the account; a duplicate returns error 247. Which of `login` and `password` are required depends on the protocol: read `required_login` and `required_password` from `retranslator/protocols/list` before saving one. A missing required value returns error 7 naming the field.\n\nRequires the `admin` right, so only a master user can call it.", "operationId": "retranslatorUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "retranslator": { "allOf": [ { "$ref": "#/components/schemas/Retranslator" } ], "description": "The retranslator to update, including `id`." } }, "required": [ "retranslator" ] } } } }, "responses": { "200": { "description": "Retranslator updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "409": { "description": "Error 247: another retranslator already has this address, port and login. Error 206: the login is already in use, which the Navixy Web Service protocol enforces across the platform.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/retranslator/delete": { "post": { "tags": [ "Tracking services" ], "summary": "Delete retranslator", "description": "Delete a retranslator. Devices forwarding through it stop doing so.\n\nRequires the `admin` right, so only a master user can call it.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "retranslatorDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "retranslator_id": { "type": "integer", "description": "ID of the retranslator to delete.", "minimum": 1 } }, "required": [ "retranslator_id" ] } } } }, "responses": { "200": { "description": "Retranslator deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/retranslator/list": { "post": { "tags": [ "Tracking services" ], "summary": "List retranslators", "description": "Return every retranslator belonging to the account.\n\n**The response includes each retranslator's `password` in clear text.**\n\nCallable with an empty body. Also accepts GET.", "operationId": "retranslatorList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": {} } } } }, "responses": { "200": { "description": "The retranslators", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Retranslator" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/retranslator/protocols/list": { "post": { "tags": [ "Tracking services" ], "summary": "List retranslator protocols", "description": "Return every retranslator protocol the platform supports, with the credential rules each one imposes.\n\nRead this before creating a retranslator: `required_login` and `required_password` decide which credentials the create call will insist on, and `fake_device_id_pattern` constrains the substitute device ID.\n\nCallable with an empty body. Also accepts GET.\n\n**The path is `retranslator/protocols/list`, plural.** The singular form `retranslator/protocol/list` returns error 111.", "operationId": "retranslatorProtocolsList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": {} } } } }, "responses": { "200": { "description": "The protocols", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/RetranslatorProtocol" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/status/create": { "post": { "tags": [ "Working statuses" ], "summary": "Create working status", "description": "Add a working status to a status list.\n\nPass the status object without `id`. An `id` that is sent anyway is ignored rather than rejected.\n\nRequires the `tracker_update` right. Requires the `statuses` tariff feature on at least one tracker. Without it the call returns error 236.", "operationId": "statusCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "listing_id": { "type": "integer", "description": "ID of the status list to add to.", "minimum": 1 }, "status": { "allOf": [ { "$ref": "#/components/schemas/WorkingStatus" } ], "description": "The working status to create, without `id`." } }, "required": [ "listing_id", "status" ] } } } }, "responses": { "200": { "description": "The created working status", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new working status." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: no tracker on the account has the `statuses` feature. Error 268: the account's quota for working statuses is exhausted.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/status/delete": { "post": { "tags": [ "Working statuses" ], "summary": "Delete working status", "description": "Delete a working status.\n\nRequires the `tracker_update` right. Requires the `statuses` tariff feature on at least one tracker. Without it the call returns error 236.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "statusDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "status_id": { "type": "integer", "description": "ID of the working status. Must belong to the current account.", "minimum": 1 } }, "required": [ "status_id" ] } } } }, "responses": { "200": { "description": "Working status deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: no tracker on the account has the `statuses` feature.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/status/list": { "post": { "tags": [ "Working statuses" ], "summary": "List working statuses", "description": "Return the working statuses of one status list, in display order.\n\nA list ID belonging to another account returns an empty array rather than an error.\n\nRequires the `statuses` tariff feature on at least one tracker. Without it the call returns error 236.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "statusList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "listing_id": { "type": "integer", "description": "ID of the status list.", "minimum": 1 } }, "required": [ "listing_id" ] } } } }, "responses": { "200": { "description": "The working statuses", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/WorkingStatus" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: no tracker on the account has the `statuses` feature.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/status/update": { "post": { "tags": [ "Working statuses" ], "summary": "Update working status", "description": "Change a working status's label or colour.\n\n**`id` is required inside the object.** Omitting it currently returns error 6 rather than a validation error.\n\nA working status cannot be moved between lists; delete it and create it again.\n\nRequires the `tracker_update` right. Requires the `statuses` tariff feature on at least one tracker. Without it the call returns error 236.", "operationId": "statusUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "allOf": [ { "$ref": "#/components/schemas/WorkingStatus" } ], "description": "The working status to update, including `id`." } }, "required": [ "status" ] } } } }, "responses": { "200": { "description": "Working status updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: no tracker on the account has the `statuses` feature.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/status/tracker/assign": { "post": { "tags": [ "Working statuses" ], "summary": "Assign working status to tracker", "description": "Set a tracker's current working status, and return the change that resulted.\n\nThe status must belong to the list assigned to that tracker, and **the list must be supervisor-controlled**: a list only its employee may change returns error 13.\n\nThe change is recorded with origin `supervisor` and the tracker's last known location. Assigning the status the tracker already holds returns error 263.\n\nNot allowed for clones. Requires the `statuses` tariff feature on at least one tracker. Without it the call returns error 236.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "statusTrackerAssign", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 }, "new_status_id": { "type": "integer", "description": "ID of the working status to assign. Must belong to the status list assigned to this tracker.", "minimum": 1 } }, "required": [ "tracker_id", "new_status_id" ] } } } }, "responses": { "200": { "description": "The resulting change", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "last_change": { "$ref": "#/components/schemas/StatusChange" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: no tracker on the account has the `statuses` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the status list does not allow a supervisor to change the status. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: the status list assigned to this tracker has no status with this ID.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/status/tracker/list": { "post": { "tags": [ "Working statuses" ], "summary": "List trackers' working statuses", "description": "Return the current working status and last change for each of the given trackers.\n\nThe result is keyed by tracker ID. `trackers` is required here, unlike the beacon calls: an empty body is rejected.\n\nRequires the `statuses` tariff feature on at least one tracker. Without it the call returns error 236.", "operationId": "statusTrackerList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Tracker IDs belonging to the current account. Cannot be an empty array.", "items": { "type": "integer" } } }, "required": [ "trackers" ] } } } }, "responses": { "200": { "description": "Current statuses by tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "object", "description": "Map of tracker ID to that tracker's current status. Keys are tracker IDs as strings.", "additionalProperties": { "$ref": "#/components/schemas/TrackerCurrentStatus" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: no tracker on the account has the `statuses` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 217: one or more of the tracker IDs does not exist or belongs to another user. Error 221: the device limit set for the account's dealer has been exceeded.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/status/tracker/read": { "post": { "tags": [ "Working statuses" ], "summary": "Read tracker's working status", "description": "Return one tracker's current working status and the last change to it.\n\nBoth may be `null`: `current_status` is null when no status is set now, and `last_change` is null when the status has never changed.\n\nRequires the `statuses` tariff feature on at least one tracker. Without it the call returns error 236.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "statusTrackerRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The tracker's working status", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "current_status": { "oneOf": [ { "$ref": "#/components/schemas/WorkingStatus" }, { "type": "null" } ], "description": "The status the tracker holds now, or `null` when none is set." }, "last_change": { "$ref": "#/components/schemas/StatusChange" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: no tracker on the account has the `statuses` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/location/link/create": { "post": { "tags": [ "Trackers" ], "summary": "Create geo link", "description": "Create a geo link, a shareable session that shows the live location of chosen trackers to someone without an account. Returns the new link's ID.\n\nA new link is **active as soon as it is created**: its `enabled` flag comes back `true` and the URL works immediately, so treat the returned hash as live from that moment. Deactivate it with `tracker/location/link/status/change`.\n\nAn account can hold up to 10,000 geo links.\n\nThe geo link object is **flattened into the request body**: its fields are sent at the top level rather than nested under an object. `hash`, `create_date`, `creator_id`, `user_id` and `enabled` are read-only. Sending them is accepted and ignored rather than refused, so it changes nothing. Do not send `hash`: it collides with the legacy `hash` authentication parameter and the request fails with error 3.\n\nRequires the `weblocator_session_create` right, which additionally needs the `weblocator` tariff feature on at least one tracker.", "operationId": "trackerLocationLinkCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "description": { "type": "string", "description": "Description of the link. At most 255 printable characters; may be empty but must be present.", "maxLength": 255 }, "trackers": { "type": "array", "description": "The trackers the link shows, and what it shows of each. 1 to 100 entries.", "items": { "$ref": "#/components/schemas/LocationLinkTracker" }, "minItems": 1, "maxItems": 100 }, "params": { "$ref": "#/components/schemas/LocationLinkParams" }, "lifetime": { "$ref": "#/components/schemas/LocationLinkLifetime" } }, "required": [ "description", "trackers", "params" ] } } } }, "responses": { "200": { "description": "The created geo link", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "integer", "description": "ID of the new geo link." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: at least one of the trackers lacks the `weblocator` tariff feature. Error 268: the account's quota for geo links is exhausted.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller lacks the required right. Error 217: one or more of the tracker IDs does not exist or belongs to another user.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: one or more of the geofences, places or place custom fields was not found.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/location/link/update": { "post": { "tags": [ "Trackers" ], "summary": "Update geo link", "description": "Replace a geo link's description, lifetime, trackers and parameters. **`id` is required**, and everything else is replaced by what is sent rather than merged.\n\nThe trackers, geofences, places and custom fields are re-checked only where the list actually changed, so an update that leaves them alone will not fail on an entity that has since become inaccessible.\n\nThe geo link object is **flattened into the request body**: its fields are sent at the top level rather than nested under an object. `hash`, `create_date`, `creator_id`, `user_id` and `enabled` are read-only. Sending them is accepted and ignored rather than refused, so it changes nothing. Do not send `hash`: it collides with the legacy `hash` authentication parameter and the request fails with error 3.\n\nRequires the `weblocator_session_create` right, which additionally needs the `weblocator` tariff feature on at least one tracker.", "operationId": "trackerLocationLinkUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the geo link to update.", "minimum": 1 }, "description": { "type": "string", "description": "Description of the link. At most 255 printable characters; may be empty but must be present.", "maxLength": 255 }, "trackers": { "type": "array", "description": "The trackers the link shows, and what it shows of each. 1 to 100 entries.", "items": { "$ref": "#/components/schemas/LocationLinkTracker" }, "minItems": 1, "maxItems": 100 }, "params": { "$ref": "#/components/schemas/LocationLinkParams" }, "lifetime": { "$ref": "#/components/schemas/LocationLinkLifetime" } }, "required": [ "id", "description", "trackers", "params" ] } } } }, "responses": { "200": { "description": "Geo link updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: at least one of the trackers lacks the `weblocator` tariff feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the caller lacks the required right. Error 217: one or more of the tracker IDs does not exist or belongs to another user.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: one or more of the geofences, places or place custom fields was not found.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/location/link/status/change": { "post": { "tags": [ "Trackers" ], "summary": "Activate or deactivate geo link", "description": "Turn a geo link on or off without deleting it. Deactivating one also closes any sessions currently open against it.\n\n**`is_active` defaults to `false`**, so a call that passes only `id` deactivates the link. Send it explicitly.\n\nThe change is recorded in the audit log. Setting the status it already has succeeds and does nothing.\n\nRequires the `weblocator_session_create` right, which additionally needs the `weblocator` tariff feature on at least one tracker.", "operationId": "trackerLocationLinkStatusChange", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the geo link.", "minimum": 1 }, "is_active": { "type": "boolean", "description": "Default `false`. `true` activates the link, `false` deactivates it." } }, "required": [ "id", "is_active" ] } } } }, "responses": { "200": { "description": "Status changed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/location/link/read": { "post": { "tags": [ "Trackers" ], "summary": "Read geo link", "description": "Return one geo link by ID, including its `hash`, which is what identifies the link to the person it was shared with.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "trackerLocationLinkRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the geo link.", "minimum": 1 } }, "required": [ "id" ] } } } }, "responses": { "200": { "description": "The geo link", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/LocationLink" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/location/link/list": { "post": { "tags": [ "Trackers" ], "summary": "List geo links", "description": "Return the account's geo links, with filtering, search conditions, sorting and pagination.\n\n`filter` and `conditions` may be combined, in which case a link must match both.\n\nCallable with an empty body. Also accepts GET.", "operationId": "trackerLocationLinkList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "filter": { "type": "string", "description": "Optional. Free-text filter applied across the searchable fields. At most 100 printable characters.", "maxLength": 100 }, "conditions": { "type": "array", "description": "Optional. Search conditions. The fields available are `trackers` (labels of all trackers on the link), `aliases`, `description`, and `creator` (the creator's full name, master user only).", "items": { "type": "object", "additionalProperties": true } }, "offset": { "type": "integer", "description": "Optional. Default 0. Offset into the results, for pagination.", "minimum": 0 }, "limit": { "type": "integer", "description": "Optional. Default and maximum 10000.", "minimum": 1, "maximum": 10000 }, "sort": { "type": "array", "description": "Optional. Default `[\"id=asc\"]`. Each entry pairs a field with a direction, for example `\"create_date=desc\"`. At most 8 entries, no duplicates. The fields available are `id`, `create_date`, `expire_date`, `status`, `trackers`, `aliases`, `description`, and `creator` (master user only). Directions are `asc` and `desc`; an entry without a direction is rejected. Sorting by `status` orders enabled, then not yet started, then expired, then disabled.", "items": { "type": "string" }, "maxItems": 8 } } } } } }, "responses": { "200": { "description": "The geo links", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/LocationLink" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/location/link/delete": { "post": { "tags": [ "Trackers" ], "summary": "Delete geo link", "description": "Delete a geo link. Anyone holding the link loses access immediately.\n\nUnlike creating, updating and switching a link, deleting one needs no specific right.", "operationId": "trackerLocationLinkDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the geo link.", "minimum": 1 } }, "required": [ "id" ] } } } }, "responses": { "200": { "description": "Geo link deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/status/listing/create": { "post": { "tags": [ "Working statuses" ], "summary": "Create working status list", "description": "Create an empty working status list. Statuses are added to it afterwards with `status/create`.\n\nPass the listing object without `id` and without `entries`: both are rejected on creation, since a new list has no statuses in it yet.\n\nRequires the `tracker_update` right. Requires the `statuses` tariff feature on at least one tracker. Without it the call returns error 236.", "operationId": "statusListingCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "listing": { "allOf": [ { "$ref": "#/components/schemas/StatusListing" } ], "description": "The status list to create, without `id` and without `entries`." } }, "required": [ "listing" ] } } } }, "responses": { "200": { "description": "The created status list", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new working status list." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: no tracker on the account has the `statuses` feature. Error 268: the account's quota for working status lists is exhausted.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/status/listing/delete": { "post": { "tags": [ "Working statuses" ], "summary": "Delete working status list", "description": "Delete a working status list and everything in it.\n\nRequires the `tracker_update` right. Requires the `statuses` tariff feature on at least one tracker. Without it the call returns error 236.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "statusListingDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "listing_id": { "type": "integer", "description": "ID of the working status list.", "minimum": 1 } }, "required": [ "listing_id" ] } } } }, "responses": { "200": { "description": "Status list deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: no tracker on the account has the `statuses` feature.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/status/listing/list": { "post": { "tags": [ "Working statuses" ], "summary": "List working status lists", "description": "Return every working status list belonging to the account, each with its `entries` in display order.\n\nCallable with an empty body. Requires the `statuses` tariff feature on at least one tracker. Without it the call returns error 236.\n\nAlso accepts GET.", "operationId": "statusListingList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": {} } } } }, "responses": { "200": { "description": "The status lists", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/StatusListing" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: no tracker on the account has the `statuses` feature.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/status/listing/update": { "post": { "tags": [ "Working statuses" ], "summary": "Update working status list", "description": "Update a working status list's label, control flags and status order.\n\n**`entries` must contain exactly the statuses already in the list**, no more and no fewer, with no duplicates. It exists to set their display order, not to add or remove statuses; a mismatched set returns error 262. Use `status/create` and `status/delete` to change membership.\n\nUpdating the list invalidates the status cache on every tracker assigned to it.\n\nRequires the `tracker_update` right. Requires the `statuses` tariff feature on at least one tracker. Without it the call returns error 236.", "operationId": "statusListingUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "listing": { "allOf": [ { "$ref": "#/components/schemas/StatusListing" } ], "description": "The status list to update, including `id` and the full `entries` array." } }, "required": [ "listing" ] } } } }, "responses": { "200": { "description": "Status list updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: no tracker on the account has the `statuses` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 262: `entries` does not match the statuses in the list, either missing some or naming ones that do not exist.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/status/listing/tracker/assign": { "post": { "tags": [ "Working statuses" ], "summary": "Assign working status list to tracker", "description": "Assign a working status list to a tracker, or remove the assignment.\n\n**Omit `listing_id` entirely to remove the assignment.** A tracker with no list assigned has no working statuses available to it.\n\nNot allowed for clones. Requires the `tracker_update` right. Requires the `statuses` tariff feature on at least one tracker. Without it the call returns error 236.\n\nAlso accepts GET with the same parameters as query-string values.", "operationId": "statusListingTrackerAssign", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 }, "listing_id": { "type": "integer", "description": "Optional. ID of the working status list to assign. **Omit to remove the current assignment.**", "minimum": 1 } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "Status list assigned", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: no tracker on the account has the `statuses` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: no working status list with this ID exists.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/route/get": { "post": { "tags": [ "Routes" ], "summary": "Get route", "description": "Return a driving route from `start` to `end`, through any `waypoints`, using a routing provider.\n\n**Omit `provider_type` to use the account's configured default provider.** The three provider-specific paths are the same operation with the provider pinned.\n\nRequires the `routing` tariff feature on **every** tracker on the account. Without it the call returns error 236.", "operationId": "routeGet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "start": { "$ref": "#/components/schemas/RoutePoint" }, "end": { "$ref": "#/components/schemas/RoutePoint" }, "waypoints": { "type": "array", "description": "Optional. Intermediate points, visited in the order given unless the provider reorders them. **At most 98**, since start and end count towards the provider's hundred-point limit.", "items": { "$ref": "#/components/schemas/RoutePoint" }, "maxItems": 98 }, "point_limit": { "type": "integer", "description": "Optional. Simplify the returned route to at most this many points.", "minimum": 2 }, "provider_type": { "type": "string", "description": "Optional. Routing provider to use. Omit to use the account's configured default.", "enum": [ "progorod", "google", "osrm", "valhalla" ] }, "minsize": { "type": "number", "description": "Optional. Default 5. Smoothing threshold in metres. Setting it below the distance between two neighbouring pixels at the current zoom is not recommended." }, "use_traffic": { "type": "boolean", "description": "Optional. Default `false`. When `false` the router runs in `optimal` mode ignoring traffic; when `true` it runs in `comfort` mode using it." } }, "required": [ "start", "end" ] } } } }, "responses": { "200": { "description": "The route", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "distance": { "type": "integer", "description": "Total length in metres." }, "time": { "type": "integer", "description": "Total duration in seconds." }, "list": { "type": "array", "description": "The route as an ordered list of points.", "items": { "$ref": "#/components/schemas/RoutePoint" } }, "key_points": { "type": "array", "description": "The start, each waypoint and the end, in that order, located within `list`. Each entry carries the cumulative `distance` and `time` from the start.", "items": { "$ref": "#/components/schemas/RouteKeyPoint" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: at least one tracker on the account lacks the `routing` tariff feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 215: the routing provider returned an error. Error 218: the routing provider rejected the parameters, most often because a point could not be matched to a road or no route exists between them. Both carry an `errors` array in the response body describing what the provider said.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/route/google/get": { "post": { "tags": [ "Routes" ], "summary": "Get route with Google", "description": "Return a route calculated by the Google Directions API. Identical to `route/get` with `provider_type` pinned to `google`; a `provider_type` sent here is ignored.\n\nGoogle allows at most 8 waypoints besides the origin and destination, or 23 for Maps API for Business customers. Exceeding that is reported as error 218 with `MAX_WAYPOINTS_EXCEEDED`.\n\nRequires the `routing` tariff feature on **every** tracker on the account. Without it the call returns error 236.", "operationId": "routeGoogleGet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "start": { "$ref": "#/components/schemas/RoutePoint" }, "end": { "$ref": "#/components/schemas/RoutePoint" }, "waypoints": { "type": "array", "description": "Optional. Intermediate points, visited in the order given unless the provider reorders them. **At most 98**, since start and end count towards the provider's hundred-point limit.", "items": { "$ref": "#/components/schemas/RoutePoint" }, "maxItems": 98 }, "point_limit": { "type": "integer", "description": "Optional. Simplify the returned route to at most this many points.", "minimum": 2 } }, "required": [ "start", "end" ] } } } }, "responses": { "200": { "description": "The route", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "distance": { "type": "integer", "description": "Total length in metres." }, "time": { "type": "integer", "description": "Total duration in seconds." }, "list": { "type": "array", "description": "The route as an ordered list of points.", "items": { "$ref": "#/components/schemas/RoutePoint" } }, "key_points": { "type": "array", "description": "The start, each waypoint and the end, in that order, located within `list`. Each entry carries the cumulative `distance` and `time` from the start, both 0 for the start point itself.", "items": { "$ref": "#/components/schemas/RouteKeyPoint" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: at least one tracker on the account lacks the `routing` tariff feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 215: the Google service failed. The `errors` array holds one of `OVER_QUERY_LIMIT`, `REQUEST_DENIED` or `UNKNOWN_ERROR`. Error 218: Google rejected the parameters. The `errors` array holds one of `NOT_FOUND`, `ZERO_RESULTS`, `MAX_WAYPOINTS_EXCEEDED` or `INVALID_REQUEST`.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/route/osrm/get": { "post": { "tags": [ "Routes" ], "summary": "Get route with OSRM", "description": "Return a route calculated by OSRM. Identical to `route/get` with `provider_type` pinned to `osrm`; a `provider_type` sent here is ignored.\n\nRequires the `routing` tariff feature on **every** tracker on the account. Without it the call returns error 236.", "operationId": "routeOsrmGet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "start": { "$ref": "#/components/schemas/RoutePoint" }, "end": { "$ref": "#/components/schemas/RoutePoint" }, "waypoints": { "type": "array", "description": "Optional. Intermediate points, visited in the order given unless the provider reorders them. **At most 98**, since start and end count towards the provider's hundred-point limit.", "items": { "$ref": "#/components/schemas/RoutePoint" }, "maxItems": 98 }, "point_limit": { "type": "integer", "description": "Optional. Simplify the returned route to at most this many points.", "minimum": 2 } }, "required": [ "start", "end" ] } } } }, "responses": { "200": { "description": "The route", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "distance": { "type": "integer", "description": "Total length in metres." }, "time": { "type": "integer", "description": "Total duration in seconds." }, "list": { "type": "array", "description": "The route as an ordered list of points.", "items": { "$ref": "#/components/schemas/RoutePoint" } }, "key_points": { "type": "array", "description": "The start, each waypoint and the end, in that order, located within `list`. Each entry carries the cumulative `distance` and `time` from the start.", "items": { "$ref": "#/components/schemas/RouteKeyPoint" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: at least one tracker on the account lacks the `routing` tariff feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 215: the OSRM service failed. Error 218: OSRM rejected the parameters. The `errors` array holds objects with `status`, either `NOT_FOUND` when a point could not be matched or no route exists, or `UNKNOWN_ERROR`; plus `status_code` and `message` passed through from OSRM, neither of which should be relied on.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/route/progorod/get": { "post": { "tags": [ "Routes" ], "summary": "Get route with Progorod", "description": "Return a route calculated by the Progorod router. Identical to `route/get` with `provider_type` pinned to `progorod`; a `provider_type` sent here is ignored.\n\n**Progorod may reorder the waypoints.** It is also the only provider that reads `minsize` and `use_traffic`; the other three accept them and ignore them.\n\nRequires the `routing` tariff feature on **every** tracker on the account. Without it the call returns error 236.", "operationId": "routeProgorodGet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "start": { "$ref": "#/components/schemas/RoutePoint" }, "end": { "$ref": "#/components/schemas/RoutePoint" }, "waypoints": { "type": "array", "description": "Optional. Intermediate points, visited in the order given unless the provider reorders them. **At most 98**, since start and end count towards the provider's hundred-point limit.", "items": { "$ref": "#/components/schemas/RoutePoint" }, "maxItems": 98 }, "point_limit": { "type": "integer", "description": "Optional. Simplify the returned route to at most this many points.", "minimum": 2 }, "minsize": { "type": "number", "description": "Optional. Default 5. Smoothing threshold in metres. Setting it below the distance between two neighbouring pixels at the current zoom is not recommended." }, "use_traffic": { "type": "boolean", "description": "Optional. Default `false`. When `false` the router runs in `optimal` mode ignoring traffic; when `true` it runs in `comfort` mode using it." } }, "required": [ "start", "end" ] } } } }, "responses": { "200": { "description": "The route", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "distance": { "type": "integer", "description": "Total length in metres." }, "time": { "type": "integer", "description": "Total duration in seconds." }, "list": { "type": "array", "description": "The route as an ordered list of points.", "items": { "$ref": "#/components/schemas/RoutePoint" } }, "key_points": { "type": "array", "description": "The start, each waypoint and the end, in that order, located within `list`. Each entry carries the cumulative `distance` and `time` from the start.", "items": { "$ref": "#/components/schemas/RouteKeyPoint" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: at least one tracker on the account lacks the `routing` tariff feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 215: the Progorod service failed. Error 218: Progorod rejected the parameters. The `errors` array holds objects with `type`, one of `not_set`, `malformed` or `isolated`; `point`, one of `start`, `end`, `waypoint` or `all`; and `index`, the position in `waypoints`, present only when `point` is `waypoint`.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/track/list": { "post": { "tags": [ "Tracks" ], "summary": "List tracks", "description": "Return the tracker's tracks over a period, with totals across all of them.\n\nWhat comes back depends on three switches. `split` decides whether the period is broken into separate trips or returned as one `merged` track. `cluster_single_reports` groups one-point trips by coordinate into `cluster` tracks. `filter` drops tracks judged too short by length and point count.\n\nPoints are left out unless `with_points` is set.", "operationId": "trackList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 }, "from": { "type": "string", "description": "Start of the period, in `YYYY-MM-DD HH:MM:SS` format, in the account's timezone.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period, in `YYYY-MM-DD HH:MM:SS` format. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "filter": { "type": "boolean", "description": "Optional. Default `true`. Drop tracks that are too short by length and number of points." }, "split": { "type": "boolean", "description": "Optional. Default `true`. When `false`, the whole period is returned as a single `merged` track." }, "include_gsm_lbs": { "type": "boolean", "description": "Optional. Default `true`. When `false`, GSM LBS points are excluded." }, "cluster_single_reports": { "type": "boolean", "description": "Optional. Default `false`. When `true`, one-point trips are grouped by coordinate into `cluster` tracks." }, "count_events": { "type": "boolean", "description": "Optional. Default `false`. When `true`, each track that is not a single point carries `event_count`." }, "omit_addresses": { "type": "boolean", "description": "Optional. Default `false`. When `true`, the address fields come back empty, which is faster." }, "with_points": { "type": "boolean", "description": "Optional. Default `false`. Include each track's `points_list`." }, "point_limit": { "type": "integer", "description": "Optional. Reduce the result to about this many points. Not a hard limit: the result can hold slightly more. Omit to use the platform's own simplification settings.", "minimum": 2, "maximum": 3000 } }, "required": [ "tracker_id", "from", "to" ] } } } }, "responses": { "200": { "description": "The tracks", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "description": "Zero or more tracks. Empty means there were no trips matching the options, or the device sent no points.", "items": { "$ref": "#/components/schemas/Track" } }, "limit_exceeded": { "type": "boolean", "description": "`true` when the requested period reaches further back than the tracker's plan allows. The period is silently clipped rather than refused, so a `true` here means the result covers less than was asked for." }, "total": { "$ref": "#/components/schemas/TrackTotals" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 211: the period between `from` and `to` exceeds the maximum the platform allows. Unlike the same code on `track/visit/list`, this one carries no `max_time_span`.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/track/read": { "post": { "tags": [ "Tracks" ], "summary": "Read track points", "description": "Return the individual points a tracker recorded over a period, or the points of one track.\n\nEach point's timestamp is when the tracker recorded it, converted to the account's timezone.\n\nReachable from a geo-link session as well as an ordinary one.", "operationId": "trackRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 }, "from": { "type": "string", "description": "Start of the period, in `YYYY-MM-DD HH:MM:SS` format, in the account's timezone.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period, in `YYYY-MM-DD HH:MM:SS` format. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "track_id": { "type": "integer", "description": "Optional. Return only the points of this track. Omit to return every valid point in the period.", "minimum": 1 }, "include_gsm_lbs": { "type": "boolean", "description": "Optional. Default `true`. When `false` **and no `track_id` is given**, GSM LBS points are excluded." }, "simplify": { "type": "boolean", "description": "Optional. Default `true`. Reduce the number of points returned." }, "point_limit": { "type": "integer", "description": "Optional. Reduce the result to about this many points. Not a hard limit: the result can hold slightly more. Omit to use the platform's own simplification settings.", "minimum": 2, "maximum": 3000 }, "filter": { "type": "boolean", "description": "Optional. Default `true`. Filter the points, which currently affects LBS tracks only. Setting it to `false` also brings parking points into the result." } }, "required": [ "tracker_id", "from", "to" ] } } } }, "responses": { "200": { "description": "The track points", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/TrackPoint" } }, "limit_exceeded": { "type": "boolean", "description": "`true` when the requested period reaches further back than the tracker's plan allows. The period is silently clipped rather than refused, so a `true` here means the result covers less than was asked for." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 211: the period between `from` and `to` exceeds the maximum the platform allows. Unlike the same code on `track/visit/list`, this one carries no `max_time_span`.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/track/download": { "post": { "tags": [ "Tracks" ], "summary": "Download track as KML", "description": "Return the tracker's points over a period as a KML or KMZ file, for opening in another mapping application.\n\n**The response is the file itself**, not a JSON envelope. The one exception is when the tracker's plan does not reach back far enough to cover any of the requested period, in which case a JSON body comes back with `limit_exceeded` set and an empty `list`.", "operationId": "trackDownload", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 }, "from": { "type": "string", "description": "Start of the period, in `YYYY-MM-DD HH:MM:SS` format, in the account's timezone.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period, in `YYYY-MM-DD HH:MM:SS` format. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "track_ids": { "type": "array", "description": "Optional. Include only these tracks. Omit to include every valid point in the period. No duplicates.", "items": { "type": "integer" } }, "include_gsm_lbs": { "type": "boolean", "description": "Optional. Default `true`. When `false` **and no `track_ids` are given**, GSM LBS points are excluded." }, "simplify": { "type": "boolean", "description": "Optional. Default `true`. Reduce the number of points in the file." }, "point_limit": { "type": "integer", "description": "Optional. Reduce the result to about this many points. Not a hard limit: the result can hold slightly more. Omit to use the platform's own simplification settings.", "minimum": 2, "maximum": 3000 }, "filter": { "type": "boolean", "description": "Optional. Default `true`. Filter the points, which currently affects LBS tracks only." }, "format": { "type": "string", "description": "Optional. Default `kml`. File format.", "enum": [ "kml", "kmz" ] }, "split": { "type": "boolean", "description": "Optional. Default `false`. When `true`, the file groups tracks into folders split by stops, each with start and end markers." } }, "required": [ "tracker_id", "from", "to" ] } } } }, "responses": { "200": { "description": "The track as a KML or KMZ file", "content": { "application/vnd.google-earth.kml+xml": { "schema": { "type": "string", "description": "The KML document." } }, "application/vnd.google-earth.kmz": { "schema": { "type": "string", "description": "The KMZ archive.", "format": "binary" } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 211: the period between `from` and `to` exceeds the maximum the platform allows. Unlike the same code on `track/visit/list`, this one carries no `max_time_span`.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/track/visit/list": { "post": { "tags": [ "Tracks" ], "summary": "List visited geofences and places", "description": "Return the IDs of the geofences and places that contain at least one of the tracker's points in the period.\n\nBoth lists come back empty when the account has no geofences and no places, and each is empty when its `include_` switch is `false`.\n\n**This call has its own period limit**, separate from the one the other track calls use, and it is usually shorter.", "operationId": "trackVisitList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 }, "from": { "type": "string", "description": "Start of the period, in `YYYY-MM-DD HH:MM:SS` format, in the account's timezone.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period, in `YYYY-MM-DD HH:MM:SS` format. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "include_zones": { "type": "boolean", "description": "Optional. Default `true`. When `false`, `zones` comes back empty." }, "include_places": { "type": "boolean", "description": "Optional. Default `true`. When `false`, `places` comes back empty." } }, "required": [ "tracker_id", "from", "to" ] } } } }, "responses": { "200": { "description": "The visited geofences and places", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "object", "properties": { "zones": { "type": "array", "description": "IDs of the geofences visited.", "items": { "type": "integer" } }, "places": { "type": "array", "description": "IDs of the places visited.", "items": { "type": "integer" } } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 211: the period exceeds this call's own maximum, which is much shorter than the one the other track calls use. The response body carries `max_time_span`, **a human-readable English string such as `26 hours`, not an ISO 8601 duration**.", "$ref": "#/components/responses/ResponseError" }, "404": { "description": "Error 204: no tracker with this ID belongs to the current account.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/track/waybill/download": { "post": { "tags": [ "Tracks" ], "summary": "Download waybill", "description": "Build a waybill for the tracker's tracks over a period and return it as a DOCX file.\n\n**This call has a side effect**: it stores the waybill number, which `track/waybill/settings/read` then returns. A number consisting only of digits is incremented before being stored, so the next waybill can carry on the sequence.\n\nIts track defaults differ from `track/list`: `include_gsm_lbs` defaults to `false` and `cluster_single_reports` to `true`, because that produces a cleaner document.", "operationId": "trackWaybillDownload", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "minimum": 1 }, "from": { "type": "string", "description": "Start of the period, in `YYYY-MM-DD HH:MM:SS` format, in the account's timezone.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period, in `YYYY-MM-DD HH:MM:SS` format. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] }, "number": { "type": "string", "description": "Waybill number. 1 to 10 characters from letters, digits, spaces and `- _ № # ? %`.", "maxLength": 10, "pattern": "^[A-Za-zА-Яа-я\\s0-9\\-_№#?%]{1,10}$" }, "series": { "type": "string", "description": "Optional. Waybill series. Same 1 to 10 character rule as `number`.", "maxLength": 10, "pattern": "^[A-Za-zА-Яа-я\\s0-9\\-_№#?%]{1,10}$" }, "type": { "type": "string", "description": "Optional. Default `form3`. Which waybill form to produce. `form3` is the passenger-car form 3, `form3ext` its extended variant for longer periods, and `form4c` and `form4cext` the freight forms.", "enum": [ "form3", "form3ext", "form4c", "form4cext" ] }, "fill_history": { "type": "boolean", "description": "Optional. Default `true`. When `false`, only the driver, garage and vehicle details are filled in, with no trips or parkings." }, "fill_odometer": { "type": "boolean", "description": "Optional. When `true`, mileage readings are written into the document's odometer fields." }, "filter": { "type": "boolean", "description": "Optional. Default `true`. Drop tracks that are too short by length and number of points." }, "split": { "type": "boolean", "description": "Optional. Default `true`. When `false`, all tracks are merged into one." }, "include_gsm_lbs": { "type": "boolean", "description": "Optional. **Default `false` here**, unlike `track/list`. When `true`, GSM LBS tracks are included." }, "cluster_single_reports": { "type": "boolean", "description": "Optional. **Default `true` here**, unlike `track/list`. When `false`, one-point trips are not grouped by coordinate." } }, "required": [ "tracker_id", "from", "to", "number" ] } } } }, "responses": { "200": { "description": "The waybill as a DOCX file", "content": { "application/vnd.openxmlformats-officedocument.wordprocessingml.document": { "schema": { "type": "string", "description": "The DOCX document.", "format": "binary" } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "402": { "description": "Error 236: the tracker's plan does not include the `app_fleet` feature.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/track/waybill/settings/read": { "post": { "tags": [ "Tracks" ], "summary": "Read last waybill number", "description": "Return the waybill number stored by the last `track/waybill/download` call, already incremented if it was all digits.\n\nReturns error 201 when the account has never downloaded a waybill.\n\nCallable with an empty body. Also accepts GET.", "operationId": "trackWaybillSettingsRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": {} } } } }, "responses": { "200": { "description": "The stored waybill number", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "object", "properties": { "number": { "type": "string", "description": "The last waybill number, incremented if it was all digits." } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/read": { "post": { "tags": [ "Trackers" ], "summary": "Read one tracker", "description": "Get a tracker by ID. Reachable from a geo link session as well as a normal one.", "operationId": "trackerRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account. **A blocked tracker is accepted here**, unlike most operations taking a tracker ID.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Tracker" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/list": { "post": { "tags": [ "Trackers" ], "summary": "List trackers", "description": "Get the account's trackers, optionally filtered by label. This is the only call that returns `tag_bindings` on every tracker. Reachable from a geo link session as well as a normal one.", "operationId": "trackerList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "labels": { "type": "array", "description": "Optional. Label filters. A tracker is returned when its label contains any one of them, so `[\"aa\", \"b\"]` matches labels `aa1` and `bb2`. Between 1 and 1024 entries, no nulls and no duplicates, each 1 to 60 characters.", "items": { "type": "string", "description": "A label fragment to match.", "minLength": 1, "maxLength": 60 }, "minItems": 1, "maxItems": 1024, "uniqueItems": true, "examples": [ [ "aa", "b" ] ] } } } } } }, "responses": { "200": { "description": "The account's trackers", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Tracker" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/list_models": { "post": { "tags": [ "Trackers" ], "summary": "List tracker models", "description": "Get every integrated tracker model. Available to demo accounts.\n\nThe list is large, around 1,800 models, so use `codes` when you already know which ones you want.", "operationId": "trackerListModels", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "compact_view": { "type": "boolean", "description": "Optional. Default `false`. Return a reduced view of each model." }, "compact_index": { "type": "boolean", "description": "Optional. Default `false`, and **deprecated**. Compact the indexed inputs, returning only the input with the highest index.", "deprecated": true }, "codes": { "type": "array", "description": "Optional. Model codes. When given, only those models are returned.", "items": { "type": "string" }, "examples": [ [ "navixy_ngp" ] ] } } } } } }, "responses": { "200": { "description": "The integrated models", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/TrackerModel" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/get_state": { "post": { "tags": [ "Trackers" ], "summary": "Read tracker state", "description": "Get the current state of one tracker: GPS, GSM, inputs, outputs, battery and movement. Reachable from a geo link or delivery session as well as a normal one.", "operationId": "trackerGetState", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The tracker state", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "user_time": { "type": "string", "description": "Current time in the account timezone." }, "state": { "$ref": "#/components/schemas/TrackerState" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/get_states": { "post": { "tags": [ "Trackers" ], "summary": "Read the state of several trackers", "description": "Get current state for many trackers at once. Reachable from a geo link session as well as a normal one.\n\n`trackers` is optional: **omitting it returns every tracker on the account**. At most 2,000 per request, so split larger fleets across calls.\n\nBy default a single blocked or missing tracker fails the whole call. `list_blocked` and `allow_not_exist` turn those into `blocked` and `not_exist` arrays alongside the results, which is almost always what a fleet-wide caller wants.", "operationId": "trackerGetStates", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Optional. Tracker IDs. Omit for every tracker on the account. At most 2,000.", "items": { "type": "integer" }, "maxItems": 2000, "examples": [ [ 999119, 999199 ] ] }, "list_blocked": { "type": "boolean", "description": "Optional. Default `false`. Return blocked tracker IDs in `blocked` instead of failing with error 208." }, "allow_not_exist": { "type": "boolean", "description": "Optional. Default `false`. Return unknown tracker IDs in `not_exist` instead of failing with error 217 or 201." } } } } } }, "responses": { "200": { "description": "The tracker states", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "user_time": { "type": "string", "description": "Current time in the account timezone." }, "states": { "type": "object", "description": "State objects keyed by tracker ID.", "additionalProperties": { "$ref": "#/components/schemas/TrackerState" } }, "blocked": { "type": "array", "description": "Blocked tracker IDs. Returned only when `list_blocked` is `true`.", "items": { "type": "integer" } }, "not_exist": { "type": "array", "description": "Unknown tracker IDs. Returned only when `allow_not_exist` is `true`.", "items": { "type": "integer" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: a tracker is corrupted and `allow_not_exist` is `false`. Error 208: a tracker is blocked and `list_blocked` is `false`. Error 217: the list names trackers that do not exist and `allow_not_exist` is `false`.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/get_last_gps_point": { "post": { "tags": [ "Trackers" ], "summary": "Read the last GPS point", "description": "Get the tracker's last point fixed by GPS. Points located by GSM LBS are excluded, so this can be older than the position in `tracker/get_state`. Reachable from a geo link session as well as a normal one.", "operationId": "trackerGetLastGpsPoint", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The last GPS point", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "object", "description": "A track point.", "properties": { "get_time": { "type": "string", "description": "GPS timestamp of the point, in the account timezone." }, "heading": { "type": "integer", "description": "Direction bearing in degrees, 0 to 360." }, "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "satellites": { "type": "integer", "description": "Number of satellites used for this fix." }, "speed": { "type": "integer", "description": "Speed in km/h." }, "precision": { "type": "integer", "description": "Precision in metres. Present only when it is not zero." } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/search_location": { "post": { "tags": [ "Trackers" ], "summary": "Search tracker location", "description": "Reverse geocoding for a tracker: return the address at its current GPS position. Same parameters and response as `geocoder/search_location`, with `tracker_id` in place of the coordinates. Reachable from a geo link session as well as a normal one.\n\nFrom a geo link session `geocoder` and `goal` are ignored: the account's geocoder is used, and a position outside the link's bounding zones counts as no position, the same rule as `tracker/get_last_gps_point`.", "operationId": "trackerSearchLocation", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "geocoder": { "type": "string", "description": "Optional. Preferred geocoder. Accounts with premium GIS always use Google regardless of this value. Ignored from a geo link session.", "enum": [ "google", "yandex", "progorod", "osm", "doublegis", "locationiq" ] }, "lang": { "type": "string", "description": "Optional. ISO 639 language code for the result, at most 5 characters, for example `en` or `en_US`. Defaults to the session language.", "maxLength": 5 }, "with_details": { "type": "boolean", "description": "Optional. Default `false`. Include the parsed `details` object in the response." }, "goal": { "type": "string", "description": "Optional. Default `ui`. What the lookup is for, which decides the geocoder chosen. Use `ui_user_action` for a lookup a user asked for directly, `ui` otherwise. **An unrecognised value is silently treated as `ui`.** Ignored from a geo link session.", "enum": [ "ui", "ui_user_action" ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The address", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "string", "description": "The address at the tracker's current position." }, "details": { "allOf": [ { "$ref": "#/components/schemas/AddressDetails" } ], "description": "Present only when `with_details` was set." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account, or the tracker has no valid GPS position yet. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/get_inputs": { "post": { "tags": [ "Trackers" ], "summary": "Read digital inputs", "description": "Get the current state of a tracker's digital inputs, and of any semantic inputs bound to them, such as ignition, buttons or car alarms. Available to demo accounts.", "operationId": "trackerGetInputs", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The input state", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "user_time": { "type": "string", "description": "Current time in the account timezone." }, "inputs": { "type": "array", "description": "State of every digital input. `[true, true, false]` means inputs 1 and 2 are on and input 3 is off.", "items": { "type": "boolean" } }, "states": { "type": "array", "description": "Semantic inputs bound to those digital inputs.", "items": { "$ref": "#/components/schemas/TrackerInputState" } }, "update_time": { "type": "string", "description": "When this data was last updated." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/batch_get_inputs": { "post": { "tags": [ "Trackers" ], "summary": "Read digital inputs for several trackers", "description": "The many-tracker form of `tracker/get_inputs`. Available to demo accounts.\n\n`trackers` is optional: **omitting it returns every tracker on the account**.", "operationId": "trackerBatchGetInputs", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Optional. Tracker IDs. Omit for every tracker on the account.", "items": { "type": "integer" }, "examples": [ [ 999199, 999919 ] ] } } } } } }, "responses": { "200": { "description": "Input state per tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "user_time": { "type": "string", "description": "Current time in the account timezone." }, "data": { "type": "object", "description": "Input info keyed by tracker ID.", "additionalProperties": { "$ref": "#/components/schemas/TrackerInputInfo" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 217: one of the listed trackers does not exist or is blocked. Error 221: too many IDs were passed.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/get_outputs": { "post": { "tags": [ "Trackers" ], "summary": "Read outputs", "description": "Get a tracker's outputs and their user-defined names. Available to demo accounts.", "operationId": "trackerGetOutputs", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The outputs", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "result": { "type": "array", "items": { "$ref": "#/components/schemas/TrackerOutput" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/batch_get_outputs": { "post": { "tags": [ "Trackers" ], "summary": "Read outputs for several trackers", "description": "The many-tracker form of `tracker/get_outputs`. Available to demo accounts.\n\n`trackers` is optional: **omitting it returns every tracker on the account**.", "operationId": "trackerBatchGetOutputs", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Optional. Tracker IDs. Omit for every tracker on the account.", "items": { "type": "integer" }, "examples": [ [ 999199, 999919 ] ] } } } } } }, "responses": { "200": { "description": "Outputs per tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "result": { "type": "object", "description": "Output lists keyed by tracker ID.", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/TrackerOutput" } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 217: one of the listed trackers does not exist or is blocked. Error 221: too many IDs were passed.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/output/update": { "post": { "tags": [ "Trackers" ], "summary": "Rename an output", "description": "Change the user-defined name of one output. This renames it only; it does not switch the output on or off.\n\nRequires the `tracker_update` right.", "operationId": "trackerOutputUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "tracker_output": { "allOf": [ { "$ref": "#/components/schemas/TrackerOutput" } ], "description": "The output to rename, identified by its `number`, carrying the new `title`." } }, "required": [ "tracker_id", "tracker_output" ] } } } }, "responses": { "200": { "description": "The output was renamed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/get_readings": { "post": { "tags": [ "Trackers" ], "summary": "Read general sensor values", "description": "Get the last values of the tracker's metering sensors, excluding CAN and OBD sensors and fuel sensors. Use `tracker/get_fuel` for fuel and `tracker/get_diagnostics` for CAN and OBD. Available to demo accounts.", "operationId": "trackerGetReadings", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The sensor readings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "user_time": { "type": "string", "description": "Current time in the account timezone." }, "inputs": { "type": "array", "items": { "$ref": "#/components/schemas/SensorValue" } }, "update_time": { "type": [ "string", "null" ], "description": "When this data was last updated. Null when the tracker has no readings of this kind." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/get_fuel": { "post": { "tags": [ "Trackers" ], "summary": "Read fuel level", "description": "Get the current fuel level of the tracker's tanks, from its fuel-related sensors. Available to demo accounts.", "operationId": "trackerGetFuel", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The fuel readings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "user_time": { "type": "string", "description": "Current time in the account timezone." }, "inputs": { "type": "array", "description": "Last readings of the fuel-related sensors.", "items": { "$ref": "#/components/schemas/SensorValue" } }, "update_time": { "type": [ "string", "null" ], "description": "When this data was last updated. Null when the tracker has no readings of this kind." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/get_diagnostics": { "post": { "tags": [ "Trackers" ], "summary": "Read CAN and OBD values", "description": "Get the last CAN and OBD sensor readings and state values received from the device. Available to demo accounts.", "operationId": "trackerGetDiagnostics", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The diagnostic readings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "user_time": { "type": "string", "description": "Current time in the account timezone." }, "inputs": { "type": "array", "items": { "$ref": "#/components/schemas/SensorValue" } }, "states": { "type": [ "object", "null" ], "description": "Last state values, keyed by state name, or null. The value types differ per state.", "additionalProperties": true }, "update_time": { "type": "string", "description": "When this data was last updated." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/tags/set": { "post": { "tags": [ "Trackers" ], "summary": "Set a tracker's tags", "description": "Replace the set of tags attached to a tracker. The tags must already exist.\n\nRequires the `tracker_update` right.", "operationId": "trackerTagsSet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "tag_bindings": { "type": "array", "description": "The tags to attach. Replaces whatever was attached before.", "items": { "$ref": "#/components/schemas/TagBinding" } } }, "required": [ "tracker_id", "tag_bindings" ] } } } }, "responses": { "200": { "description": "The tags were set", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/corrupt": { "post": { "tags": [ "Trackers" ], "summary": "Corrupt a tracker", "description": "Mark a tracker deleted and corrupt its source, device ID and phone, freeing that device ID and phone number for reuse.\n\nThis is not reversible. The tracker must not be currently connected to the server.\n\nRequires the `tracker_register` right.", "operationId": "trackerCorrupt", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The tracker was corrupted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the tracker is already connected to the server, or the user lacks the right. Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 219: the tracker is itself a clone. Error 243: the device is already connected. Error 252: the device is already corrupted.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/delete": { "post": { "tags": [ "Trackers" ], "summary": "Delete a clone", "description": "Delete a tracker, but only when it is a clone. Passing the ID of an original tracker fails with error 249; use `tracker/corrupt` for those.\n\nError 203 carries the blocking references in the response body, as a `rules` or `vehicles` array of IDs. Read it to find what to detach first.\n\nRequires the `admin` right.", "operationId": "trackerDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account. **A blocked tracker is accepted here**, unlike most operations taking a tracker ID.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The clone was deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 203: rules or vehicles are still associated with the tracker, and the body names them. Error 249: the tracker is not a clone.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/change_phone": { "post": { "tags": [ "Trackers" ], "summary": "Change the SIM phone and APN", "description": "Change the phone number of the SIM in the device and set the matching APN.\n\nRequires the `tracker_configure` right.", "operationId": "trackerChangePhone", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account. **A blocked tracker is accepted here**, unlike most operations taking a tracker ID.", "examples": [ 123456 ] }, "phone": { "type": "string", "description": "Phone number of the SIM, in international format without a leading `+`.", "examples": [ "6156680000" ] }, "apn_name": { "type": "string", "description": "GPRS APN name for the SIM.", "maxLength": 40, "examples": [ "fast.tmobile.com" ] }, "apn_user": { "type": "string", "description": "GPRS APN user. May be empty.", "maxLength": 40, "examples": [ "tmobile" ] }, "apn_password": { "type": "string", "description": "GPRS APN password. May be empty.", "maxLength": 40, "examples": [ "tmobile" ] } }, "required": [ "tracker_id", "phone" ] } } } }, "responses": { "200": { "description": "The phone and APN were changed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: the device has no GSM module. Error 219: the tracker is a clone. Error 223: the phone number is already used by another device. Error 241: the number belongs to a SIM bundled with the device, which cannot be changed here.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/location_request": { "post": { "tags": [ "Trackers" ], "summary": "Request the current location", "description": "Ask the device for its current position. The model must support the requested method, which `tracker/list_models` reports as `has_location_request`, `has_gprs_location_request` and `has_gsm_lbs_location_request`.\n\nThe `sms` method sends an SMS and therefore needs an SMS gateway installed for the panel; it may also cost money. The `gsm` and `gprs` methods need the device online or in the `GPS not updated` state.", "operationId": "trackerLocationRequest", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "type": { "type": "string", "description": "Optional. Default `sms`. How to ask: `sms` for GNSS data over SMS, `gsm` for GSM LBS data over GPRS, `gprs` for GNSS data over GPRS.", "enum": [ "sms", "gsm", "gprs" ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The request was sent", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 213: the device is offline. Error 214: the device does not support this request type. Error 256: the location is already current.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/register": { "post": { "tags": [ "Trackers" ], "summary": "Register a tracker", "description": "Register a new device and link it to the current account, configuring it to send data to the platform where the model supports that. The panel must have an SMS gateway installed.\n\n**Registration plugins matter.** Each plugin has its own extra parameters, and you must send those alongside the ones below. `commons/plugin` lists them. A Queclink registered through plugin 37, for example, also needs `phone`, `apn_name`, `apn_user`, `apn_password` and `activation_code`.\n\n`device_id` is validated in two stages, both returning error 7. First a general character and length check, reported as `must match \"^[0-9a-zA-Z\\-]{1,64}$\"`, which rejects underscores, spaces, punctuation and non-latin characters. Then the length range the model's `id_type` requires. **Read the range from the error message rather than from the model metadata**, which can be wider: models declaring `ascii,6,64` reject anything over 32 characters.\n\nRequires the `tracker_register` right.", "operationId": "trackerRegister", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "label": { "type": "string", "description": "User-defined label. Printable characters, 1 to 60.", "minLength": 1, "maxLength": 60, "examples": [ "Courier" ] }, "group_id": { "type": "integer", "description": "Tracker group ID, or `0` for no group. The group must exist.", "examples": [ 0 ] }, "model": { "type": "string", "description": "Code of a supported model, from `tracker/list_models`.", "examples": [ "qlgv55lite" ] }, "plugin_id": { "type": "integer", "description": "ID of the registration plugin to use.", "examples": [ 37 ] }, "device_id": { "type": "string", "description": "Required when the model uses a fixed device ID. The accepted format and length follow the model's `id_type`.", "examples": [ "123451234512346" ] }, "send_register_commands": { "type": [ "boolean", "null" ], "description": "Optional. Default null, meaning follow the platform setting. Whether to send activation commands to the device over SMS or GPRS." } }, "required": [ "label", "group_id", "model", "plugin_id" ], "additionalProperties": true } } } }, "responses": { "200": { "description": "The registered tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Tracker" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the user lacks the right. Error 204: the group does not exist. Error 220: unknown device model. Error 221: the dealer's device limit is exceeded. Error 222: the plugin was not found or does not support this model. Error 223: the phone number is already in use. Error 224: the device ID is already registered. Error 225: the new device's tariff is not compatible with the account's legal type. Error 226: unknown ICCID, plugin specific. Error 227: the activation code is unknown or already used, plugin specific. Error 258: no bundle for this device ID, plugin specific.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/register_quick": { "post": { "tags": [ "Trackers" ], "summary": "Register a preconfigured device by IMEI", "description": "Register a new device using only its IMEI. No activation commands are sent, so the device must already be configured. **Bundles only.**\n\nRequires the `tracker_register` right.", "operationId": "trackerRegisterQuick", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "label": { "type": "string", "description": "User-defined label. Printable characters, 1 to 60.", "minLength": 1, "maxLength": 60, "examples": [ "Courier" ] }, "group_id": { "type": "integer", "description": "Tracker group ID, or `0` for no group. The group must exist.", "examples": [ 0 ] }, "imei": { "type": "string", "description": "IMEI of the device.", "examples": [ "35645587458999" ] } }, "required": [ "label", "group_id", "imei" ] } } } }, "responses": { "200": { "description": "The registered tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Tracker" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the user lacks the right. Error 201: no bundle with this IMEI. Error 204: the group does not exist. Error 220: unknown device model. Error 221: the dealer's device limit is exceeded. Error 222: the plugin was not found or does not support this model. Error 223: the phone number is already in use. Error 224: the device ID is already registered. Error 225: the tariff is not compatible with the account's legal type. Error 226: unknown ICCID. Error 227: the activation code is unknown or already used.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/register_retry": { "post": { "tags": [ "Trackers" ], "summary": "Resend registration commands", "description": "Resend the registration commands to a device that did not come online. The panel must have an SMS gateway installed.\n\nOnly `tracker_id` is required: the APN fields are optional and change the stored APN when sent.\n\nRequires the `tracker_register` right.", "operationId": "trackerRegisterRetry", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "device_id": { "type": "string", "description": "Optional. The device ID used at registration, such as the IMEI. Usable instead of `tracker_id` for models with a fixed ID.", "examples": [ "4568005588562" ] }, "apn_name": { "type": "string", "description": "Optional. GPRS APN name for the SIM.", "maxLength": 40, "examples": [ "fast.tmobile.com" ] }, "apn_user": { "type": "string", "description": "Optional. GPRS APN user. May be empty.", "maxLength": 40 }, "apn_password": { "type": "string", "description": "Optional. GPRS APN password. May be empty.", "maxLength": 40 }, "send_register_commands": { "type": [ "boolean", "null" ], "description": "Optional. Default null, meaning follow the platform setting." } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Tracker" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the user lacks the right. Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: the device has no GSM module. Error 219: the tracker is a clone. Error 242: the device is already connected.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/replace": { "post": { "tags": [ "Trackers" ], "summary": "Replace the device behind a tracker", "description": "Swap in a new device while keeping the tracker's history, and optionally its sensors and rules.\n\nAs with registration, the chosen plugin brings its own extra parameters that must be sent alongside these. An activation code is **not** used when replacing.\n\nRequires the `tracker_configure` right.", "operationId": "trackerReplace", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "model": { "type": "string", "description": "Code of a supported model, from `tracker/list_models`.", "examples": [ "qlgv55lite" ] }, "plugin_id": { "type": "integer", "description": "ID of the registration plugin to use.", "examples": [ 37 ] }, "device_id": { "type": "string", "description": "Required when the model uses a fixed device ID.", "examples": [ "123451234512346" ] }, "send_register_commands": { "type": [ "boolean", "null" ], "description": "Optional. Default null, meaning follow the platform setting." } }, "required": [ "tracker_id", "model", "plugin_id" ], "additionalProperties": true } } } }, "responses": { "200": { "description": "The replaced tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Tracker" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the user lacks the right. Error 204: the group does not exist. Error 220: unknown device model. Error 221: the dealer's device limit is exceeded. Error 222: the plugin was not found or does not support this model. Error 223: the phone number is already in use. Error 224: the device ID is already registered. Error 225: the tariff is not compatible with the account's legal type. Error 226: unknown ICCID, plugin specific. Error 258: no bundle for this device ID, plugin specific. Error 266: the device is not activated yet.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/replace_quick": { "post": { "tags": [ "Trackers" ], "summary": "Replace a device by IMEI", "description": "Replace the device behind a tracker using only the new IMEI. No activation commands are sent, so the replacement must already be configured. **Bundles only.**\n\nRequires the `tracker_configure` right.", "operationId": "trackerReplaceQuick", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "imei": { "type": "string", "description": "IMEI of the new device.", "examples": [ "35645587458999" ] } }, "required": [ "tracker_id", "imei" ] } } } }, "responses": { "200": { "description": "The replaced tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Tracker" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the user lacks the right. Error 201: no bundle with this IMEI. Error 204: the group does not exist. Error 220: unknown device model. Error 221: the dealer's device limit is exceeded. Error 222: the plugin was not found or does not support this model. Error 223: the phone number is already in use. Error 224: the device ID is already registered. Error 225: the tariff is not compatible with the account's legal type. Error 226: unknown ICCID. Error 266: the device is not activated yet.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/replace_retry": { "post": { "tags": [ "Trackers" ], "summary": "Resend replacement commands", "description": "Resend the registration commands to the replacement device. The panel must have an SMS gateway installed.\n\nRequires the `tracker_configure` right.", "operationId": "trackerReplaceRetry", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "apn_name": { "type": "string", "description": "Optional. GPRS APN name for the SIM.", "examples": [ "fast.tmobile.com" ] }, "apn_user": { "type": "string", "description": "Optional. GPRS APN user.", "examples": [ "tmobile" ] }, "apn_password": { "type": "string", "description": "Optional. GPRS APN password.", "examples": [ "tmobile" ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Tracker" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the user lacks the right. Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: the device has no GSM module. Error 219: the tracker is a clone. Error 242: the device is already connected. Error 266: the old device is not activated yet.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/send_command": { "post": { "tags": [ "Trackers" ], "summary": "Send a special control command", "description": "Send a command for the special control the model declares in its `special_control` field.\n\nWhich commands are available follows from that field. `jointech_lock_password`, `hhd_lock_password` and `vg_lock_password` accept `electronic_lock_command` and `set_special_settings_command`; every other special control accepts only `set_special_settings_command`.\n\n`electronic_lock_command` seals or unseals an electronic lock and takes a `command_code` of `seal` or `unseal`. The optional `special_settings` object has the same shape as the body of `tracker/settings/special/update`.\n\nRequires the `tracker_configure` and `tracker_set_output` rights.", "operationId": "trackerSendCommand", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "command": { "type": "object", "description": "The command to send.", "properties": { "name": { "type": "string", "description": "Command name.", "enum": [ "electronic_lock_command", "set_special_settings_command" ] }, "command_code": { "type": "string", "description": "For `electronic_lock_command`: whether to seal or unseal.", "enum": [ "seal", "unseal" ] }, "special_settings": { "type": "object", "description": "Optional. Special settings for the device, shaped by the model's `special_control`.", "additionalProperties": true } }, "required": [ "name" ], "additionalProperties": true } }, "required": [ "tracker_id", "command" ] } } } }, "responses": { "200": { "description": "The command was accepted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/raw_command/send": { "post": { "tags": [ "Trackers" ], "summary": "Send a raw GPRS command", "description": "Send a GPRS command to the device, processed beforehand in a protocol-dependent way.\n\nRequires the `tracker_configure` and `tracker_set_output` rights.", "operationId": "trackerRawCommandSend", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "command": { "type": "string", "description": "The command, as text or hexadecimal.", "examples": [ "AT+GTRTO=gv200,A,,,,,,0001$" ] }, "type": { "type": "string", "description": "Optional. Default `text`. How to read `command`.", "enum": [ "text", "hex" ] }, "reliable": { "type": "boolean", "description": "Optional. Default `true`. When `false` the command is not resent if the device is disconnected or does not acknowledge it." } }, "required": [ "tracker_id", "command" ] } } } }, "responses": { "200": { "description": "The command was accepted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this device ID belongs to the account.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/settings/read": { "post": { "tags": [ "Trackers" ], "summary": "Read label and group", "description": "Get the tracker's label and group. Available to demo accounts.", "operationId": "trackerSettingsRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The base settings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "settings": { "$ref": "#/components/schemas/TrackerBaseSettings" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/settings/update": { "post": { "tags": [ "Trackers" ], "summary": "Change label and group", "description": "Change the tracker's label and group.\n\n**Both `label` and `group_id` are required**, so this call always writes both. Read the current values first if you mean to change only one.\n\nRequires the `tracker_update` right.", "operationId": "trackerSettingsUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "group_id": { "type": "integer", "description": "Tracker group ID, or `0` for no group. The group must exist.", "examples": [ 1 ] }, "label": { "type": "string", "description": "User-defined label. Printable characters, 1 to 60, and it cannot contain `<` or `>`.", "minLength": 1, "maxLength": 60, "examples": [ "Courier" ] } }, "required": [ "tracker_id", "group_id", "label" ] } } } }, "responses": { "200": { "description": "The settings were changed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 204: no group with this ID. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/settings/lbs/read": { "post": { "tags": [ "Trackers" ], "summary": "Read the LBS radius limit", "description": "Get the maximum radius accepted for LBS points on this tracker. LBS positions a device from cell towers or Wi-Fi access points rather than from GNSS, so its points carry a much larger uncertainty than a GPS fix. Available to demo accounts.", "operationId": "trackerSettingsLbsRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The LBS radius limit", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "max_radius": { "type": "integer", "description": "Maximum accepted radius for LBS points, in metres.", "minimum": 0, "maximum": 10000, "examples": [ 300 ] } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/settings/lbs/update": { "post": { "tags": [ "Trackers" ], "summary": "Change the LBS radius limit", "description": "Change the maximum radius accepted for LBS points. Points reported with a larger radius than this are discarded, so raising it admits vaguer positions and lowering it discards more of them.\n\nRequires the `tracker_update` right.", "operationId": "trackerSettingsLbsUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "max_radius": { "type": "integer", "description": "Maximum accepted radius for LBS points, in metres.", "minimum": 0, "maximum": 10000, "examples": [ 1000 ] } }, "required": [ "tracker_id", "max_radius" ] } } } }, "responses": { "200": { "description": "The limit was changed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/settings/tracking/read": { "post": { "tags": [ "Trackers" ], "summary": "Read tracking mode settings", "description": "Get the device's data transmission and operating mode settings. Available to demo accounts.\n\n**The fields differ from model to model**, so the response is an open object rather than a fixed shape. The tracking profiles page lists what each model accepts. A model with no tracking settings at all answers error 214.", "operationId": "trackerSettingsTrackingRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The tracking settings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "object", "description": "The model's tracking settings. Keys depend entirely on the model.", "additionalProperties": true } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: this model has no tracking settings.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/settings/tracking/update": { "post": { "tags": [ "Trackers" ], "summary": "Change tracking mode settings", "description": "Send new data transmission and operating mode settings to the device.\n\n**This reaches the hardware**, so it is not a database-only write. The accepted fields differ from model to model; see the tracking profiles page for what a given model takes.\n\nRequires the `tracker_configure` right.", "operationId": "trackerSettingsTrackingUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "tracking_settings": { "type": "object", "description": "The settings to send. Keys depend on the model.", "additionalProperties": true, "examples": [ { "tracking_angle": 30, "tracking_distance": 100, "tracking_interval": 60, "on_stop_tracking_interval": 180, "sleep_mode": "disabled", "stop_detection": "ignition" } ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The settings were sent", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: this model has no tracking settings. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/settings/trip_detection/read": { "post": { "tags": [ "Trackers" ], "summary": "Read parking detection settings", "description": "Get how the platform decides this tracker has stopped. These settings split a trajectory into trips and drive the trip start and end events. Available to demo accounts.", "operationId": "trackerSettingsTripDetectionRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The parking detection settings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "min_idle_duration_minutes": { "type": "integer", "description": "Minutes the device must stay idle before a trip counts as finished.", "examples": [ 5 ] }, "idle_speed_threshold": { "type": "integer", "description": "Speed in km/h below which the device counts as idle.", "examples": [ 3 ] }, "ignition_aware": { "type": "boolean", "description": "Whether the ignition state is considered." }, "motion_sensor_aware": { "type": "boolean", "description": "Whether the motion sensor is considered." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/settings/trip_detection/update": { "post": { "tags": [ "Trackers" ], "summary": "Change parking detection settings", "description": "Change how the platform decides this tracker has stopped.\n\n`min_idle_duration_minutes` and `idle_speed_threshold` are both required; `ignition_aware` and `motion_sensor_aware` are optional.\n\nRequires the `tracker_update` right.", "operationId": "trackerSettingsTripDetectionUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "min_idle_duration_minutes": { "type": "integer", "description": "Minutes the device must stay idle before a trip counts as finished.", "minimum": 1, "maximum": 1440, "examples": [ 5 ] }, "idle_speed_threshold": { "type": "integer", "description": "Speed in km/h below which the device counts as idle. `0` means it never counts as idle.", "minimum": 0, "maximum": 200, "examples": [ 3 ] }, "ignition_aware": { "type": "boolean", "description": "Optional. Consider the ignition state when detecting a trip." }, "motion_sensor_aware": { "type": "boolean", "description": "Optional. Consider the motion sensor when detecting a trip." } }, "required": [ "tracker_id", "min_idle_duration_minutes", "idle_speed_threshold" ] } } } }, "responses": { "200": { "description": "The settings were changed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/settings/special/read": { "post": { "tags": [ "Trackers" ], "summary": "Read special settings", "description": "Get the model-specific special settings for a tracker. Available to demo accounts.\n\n**The response shape depends on whether you pass `type`.** With it, the settings object comes back under `value`; without it, every special-settings object the tracker has comes back under `list`.", "operationId": "trackerSettingsSpecialRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "type": { "type": "string", "description": "Optional. Which kind of special settings to read. Omit to get all of them.", "examples": [ "electronic_lock_password" ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The special settings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "allOf": [ { "$ref": "#/components/schemas/SpecialSettings" } ], "description": "Returned when `type` was given." }, "list": { "type": "array", "description": "Returned when `type` was omitted.", "items": { "$ref": "#/components/schemas/SpecialSettings" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: the device does not support this operation.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/settings/special/update": { "post": { "tags": [ "Trackers" ], "summary": "Change special settings", "description": "Replace one special-settings object on a tracker.\n\n**This reaches the hardware.** The `value` object must carry the `type` that identifies which settings are being written.\n\nRequires the `tracker_configure` right.", "operationId": "trackerSettingsSpecialUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "value": { "allOf": [ { "$ref": "#/components/schemas/SpecialSettings" } ], "description": "The settings object to write, including its `type`." } }, "required": [ "tracker_id", "value" ] } } } }, "responses": { "200": { "description": "The settings were sent", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: the device does not support this operation.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/apn_settings/read": { "post": { "tags": [ "Trackers" ], "summary": "Read APN settings by tracker", "description": "Get the APN name, user and password configured for a tracker's SIM. The APN is what tells the device how to reach the mobile network.\n\nThis is the by-tracker form. `apn_settings/read`, at the top level, does the same thing by phone number and needs no authentication.\n\nRequires the `tracker_register` right.", "operationId": "trackerApnSettingsRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The APN settings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "object", "description": "The APN configuration.", "properties": { "name": { "type": "string", "description": "APN name.", "examples": [ "fast.tmobile.com" ] }, "user": { "type": "string", "description": "APN user.", "examples": [ "tmobile" ] }, "password": { "type": "string", "description": "APN password.", "examples": [ "tmobile" ] } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker, or no APN settings for it. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: the device has no GSM module, or uses a bundled SIM whose details are hidden from the user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/stats/engine_hours/read": { "post": { "tags": [ "Trackers" ], "summary": "Read engine hours", "description": "Get the engine hours counted for a period, meaning the time the engine was on.\n\nThe device needs an ignition input; models without one answer error 214.", "operationId": "trackerStatsEngineHoursRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "from": { "type": "string", "description": "Start of the period, `YYYY-MM-DD HH:MM:SS`, in the account timezone.", "examples": [ "2020-09-24 03:24:00" ] }, "to": { "type": "string", "description": "End of the period. Must be after `from`.", "examples": [ "2020-09-24 06:24:00" ] } }, "required": [ "tracker_id", "from", "to" ] } } } }, "responses": { "200": { "description": "The engine hours", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "number", "description": "Engine hours counted for the period.", "examples": [ 42 ] } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 211: the period is longer than the platform allows. Error 214: the device has no ignition input. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/stats/mileage/read": { "post": { "tags": [ "Trackers" ], "summary": "Read mileage per day", "description": "Get mileage in kilometres for a period, grouped by tracker and then by day. Available to demo accounts.\n\n**A day with no data comes back as `null`, not as zero.** That distinction matters: `null` means nothing was recorded, while `{\"mileage\": 0.0}` means the device was tracked and did not move.", "operationId": "trackerStatsMileageRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Tracker IDs. At most 128 per request.", "items": { "type": "integer" }, "maxItems": 128, "examples": [ [ 123456 ] ] }, "from": { "type": "string", "description": "Start of the period, `YYYY-MM-DD HH:MM:SS`, in the account timezone.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the period. Must be after `from`.", "examples": [ "2026-08-24 09:00:00" ] } }, "required": [ "trackers", "from", "to" ] } } } }, "responses": { "200": { "description": "The mileage", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "result": { "type": "object", "description": "Keyed by tracker ID, then by date.", "additionalProperties": { "type": "object", "description": "Keyed by date, `YYYY-MM-DD`.", "additionalProperties": { "type": [ "object", "null" ], "description": "Mileage for that day, or null when nothing was recorded.", "properties": { "mileage": { "type": "number", "description": "Distance travelled that day, in kilometres.", "examples": [ 199.09 ] } } } } }, "limit_exceeded": { "type": "boolean", "description": "`true` when the period was clipped because it exceeded what the plan covers." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 211: the period is longer than the platform allows. Error 217: the list names trackers that do not exist. Error 221: the dealer's device limit is exceeded.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/avatar/upload": { "post": { "tags": [ "Trackers" ], "summary": "Upload a tracker avatar", "description": "Upload an avatar image for a tracker. **Multipart request**, with the image in a part named `file`.\n\nAccepted MIME types are `image/jpeg`, `image/pjpeg`, `image/png`, `image/gif` and `image/webp`. Once uploaded the image is served from `/[api_static_path]/tracker/avatars/`, for example `https://api.eu.navixy.com/v2/static/tracker/avatars/abcdef123456789.png`.\n\nRequires the `tracker_update` right.", "operationId": "trackerAvatarUpload", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "file": { "type": "string", "description": "The image file.", "format": "binary" }, "redirect_target": { "type": "string", "description": "Optional. URL to redirect to. When given, the response is a redirect to `?response=` instead of a JSON body." } }, "required": [ "tracker_id", "file" ] } } } }, "responses": { "200": { "description": "The uploaded avatar", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "string", "description": "File name of the stored avatar." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 233: no file part was sent. Error 234: the file has an unaccepted MIME type. Error 254: the file could not be saved.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/datalogger/upload": { "post": { "tags": [ "Trackers" ], "summary": "Upload datalogger track data", "description": "Upload recorded track data for a datalogger. **Multipart request**, with a CSV file in a part named `file`.\n\nThe tracker must be a datalogger; other models answer error 214.", "operationId": "trackerDataloggerUpload", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "file": { "type": "string", "description": "A CSV file of datalogger track data.", "format": "binary" } }, "required": [ "tracker_id", "file" ] } } } }, "responses": { "200": { "description": "The data was accepted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 214: the tracker is not a datalogger. Error 219: the tracker is a clone. Error 233: no file part was sent.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/mobile/register": { "post": { "tags": [ "Trackers" ], "summary": "Register a mobile application (deprecated)", "description": "**Deprecated. Do not use.** Register a new mobile client application.\n\nUse `tracker/register` with `plugin_id` 35 instead. As with that call, the chosen registration plugin adds its own parameters on top of the ones below.\n\nRequires the `tracker_register` right.", "operationId": "trackerMobileRegister", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "label": { "type": "string", "description": "User-defined label. Printable characters, 1 to 60.", "minLength": 1, "maxLength": 60, "examples": [ "Courier" ] }, "group_id": { "type": "integer", "description": "Tracker group ID, or `0` for no group. The group must exist.", "examples": [ 0 ] }, "device_id": { "type": "string", "description": "Required when the model uses a fixed device ID.", "examples": [ "4568005588562" ] }, "send_register_commands": { "type": [ "boolean", "null" ], "description": "Optional. Default null, meaning follow the platform setting." } }, "required": [ "label", "group_id" ], "additionalProperties": true } } } }, "responses": { "200": { "description": "The registered tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Tracker" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 13: the user lacks the right. Error 204: the group does not exist. Error 221: the dealer's device limit is exceeded. Error 224: the device ID is already registered. Error 225: the tariff is not compatible with the account's legal type.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/alarm_mode/read": { "post": { "tags": [ "Trackers" ], "summary": "Read alarm mode", "description": "Get whether alarm mode is on for a device. Available to demo accounts. Models that do not support alarm mode answer error 214.", "operationId": "trackerAlarmModeRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The alarm mode state", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "enabled": { "type": "boolean", "description": "`true` when alarm mode is on." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: the device does not support alarm mode.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/alarm_mode/set": { "post": { "tags": [ "Trackers" ], "summary": "Turn alarm mode on or off", "description": "Change alarm mode on a device. **The device must be online**, since this reaches the hardware; an offline device answers error 213.", "operationId": "trackerAlarmModeSet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "enabled": { "type": "boolean", "description": "Whether alarm mode should be on." } }, "required": [ "tracker_id", "enabled" ] } } } }, "responses": { "200": { "description": "The change was sent", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 213: the device is offline. Error 214: the device does not support alarm mode. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/chat/list": { "post": { "tags": [ "Trackers" ], "summary": "List chat messages", "description": "Get chat messages for one tracker. Defaults to the last seven days when no period is given.", "operationId": "trackerChatList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "from": { "type": "string", "description": "Optional. Start of the period. Defaults to seven days ago.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "Optional. End of the period. Defaults to now.", "examples": [ "2026-08-24 09:00:00" ] }, "limit": { "type": "integer", "description": "Optional. Maximum messages to return. Both the default and the maximum are 1024.", "maximum": 1024, "examples": [ 1024 ] }, "ascending": { "type": "boolean", "description": "Optional. Default `true`. Order from oldest message to newest." } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The messages", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/ChatMessage" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: the device does not support this operation. Error 236: the tariff does not include the chat feature.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/chat/send": { "post": { "tags": [ "Trackers" ], "summary": "Send a chat message", "description": "Send a chat message to one tracker.", "operationId": "trackerChatSend", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "message": { "type": "string", "description": "Message text.", "maxLength": 20000, "examples": [ "Hello World" ] } }, "required": [ "tracker_id", "message" ] } } } }, "responses": { "200": { "description": "The message was submitted", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the submitted message.", "examples": [ 222 ] } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: the device does not support this operation. Error 236: the tariff does not include the chat feature.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/chat/broadcast": { "post": { "tags": [ "Trackers" ], "summary": "Send a chat message to several trackers", "description": "Send the same message to many trackers at once.\n\nThe response reports delivery per tracker rather than failing as a whole, so read `not_sent_to` even on success.", "operationId": "trackerChatBroadcast", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Tracker IDs. At most 300.", "items": { "type": "integer" }, "maxItems": 300, "examples": [ [ 999199, 999919 ] ] }, "message": { "type": "string", "description": "Message text.", "maxLength": 20000, "examples": [ "Hello World" ] } }, "required": [ "trackers", "message" ] } } } }, "responses": { "200": { "description": "The broadcast result", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "sent_to": { "type": "array", "description": "Trackers the message reached.", "items": { "type": "integer" } }, "not_sent_to": { "type": "array", "description": "Trackers the message failed to reach.", "items": { "type": "integer" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 217: the list names trackers that do not exist, are blocked, or lack the required tariff feature. Error 221: the dealer's device limit is exceeded.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/chat/mark_read": { "post": { "tags": [ "Trackers" ], "summary": "Mark messages read", "description": "Mark one or several incoming messages as read.\n\nUse one of `message_id` and `message_ids`. In practice either works on its own and **sending both is accepted rather than refused**, so treat the single-parameter rule as guidance, not as enforcement.", "operationId": "trackerChatMarkRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "message_id": { "type": "integer", "description": "ID of a single incoming message.", "examples": [ 123 ] }, "message_ids": { "type": "array", "description": "IDs of several incoming messages.", "items": { "type": "integer" }, "examples": [ [ 123, 213 ] ] } } } } } }, "responses": { "200": { "description": "The messages were marked read", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no such message.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/chat/mark_read_all": { "post": { "tags": [ "Trackers" ], "summary": "Mark all messages read", "description": "Mark every incoming message read.\n\n**`trackers` is optional, and omitting it marks messages read on every tracker on the account.** There is no dry run and no undo, so send the list unless you genuinely mean all of them.", "operationId": "trackerChatMarkReadAll", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Optional. Tracker IDs. Omit to affect every tracker on the account.", "items": { "type": "integer" }, "examples": [ [ 999199, 999919 ] ] } } } } } }, "responses": { "200": { "description": "The messages were marked read", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: not found in the database.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/chat/updated/list": { "post": { "tags": [ "Trackers" ], "summary": "Read last message times", "description": "Get the time of the most recent chat message for each of the given trackers, so a client can tell which conversations have moved without fetching them.", "operationId": "trackerChatUpdatedList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Tracker IDs. At most 300.", "items": { "type": "integer" }, "maxItems": 300, "examples": [ [ 999199, 999919 ] ] } }, "required": [ "trackers" ] } } } }, "responses": { "200": { "description": "Last message times", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "object", "description": "Timestamps keyed by tracker ID.", "additionalProperties": { "type": "string", "description": "When the last message was sent." } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 217: the list names trackers that do not exist, are blocked, or lack the required tariff feature. Error 221: the dealer's device limit is exceeded.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/chat/unread/count": { "post": { "tags": [ "Trackers" ], "summary": "Count unread messages", "description": "Get the number of unread chat messages, grouped by tracker. Takes no parameters and covers the whole account.", "operationId": "trackerChatUnreadCount", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": {} } } } }, "responses": { "200": { "description": "Unread counts", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "object", "description": "Counts keyed by tracker ID.", "additionalProperties": { "type": "integer", "description": "Unread messages for that tracker." } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 236: no tracker on the account has a tariff including the chat feature.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/command/create": { "post": { "tags": [ "Trackers" ], "summary": "Create a command", "description": "Define a reusable command on a tracker. Commands are stored per tracker and executed on demand.\n\nA tracker holds **at most 10 commands**, and an eleventh is rejected. Commands cannot be created on a clone device.\n\nA configuration may contain **at most one `<>` placeholder**, and one with more than one is rejected. See the dynamic values note on execute.\n\nFor automated, rule-based command sending across many devices, use IoT Logic's device action or webhook nodes instead; this family is for manual, single-tracker actions.\n\nRequires the `tracker_update` right.", "operationId": "trackerCommandCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "command": { "allOf": [ { "$ref": "#/components/schemas/TrackerCommand" } ], "description": "The command to create, without `id`." } }, "required": [ "tracker_id", "command" ] } } } }, "responses": { "200": { "description": "The created command", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new command.", "examples": [ 19 ] } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: the command or the tracker does not exist.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/command/update": { "post": { "tags": [ "Trackers" ], "summary": "Update a command", "description": "Replace an existing command. **The whole object must be sent, including its `id`**; there is no partial update.\n\nAs with create, the configuration may contain **at most one `<>` placeholder**, and one with more than one is rejected.\n\nRequires the `tracker_update` right.", "operationId": "trackerCommandUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "command": { "allOf": [ { "$ref": "#/components/schemas/TrackerCommand" } ], "description": "The full command object, including `id`." } }, "required": [ "tracker_id", "command" ] } } } }, "responses": { "200": { "description": "The command was updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: the command or the tracker does not exist.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/command/execute": { "post": { "tags": [ "Trackers" ], "summary": "Execute a command", "description": "Run a stored command immediately. A `hardware` command sends its string to the device; an `http` command dispatches the POST request with live attribute values substituted into the body.\n\n**Dynamic values.** Where the command's configuration contains a `<>` placeholder, the value that replaces it is supplied here as `param`, and the substitution happens before the command is sent. `param` is **required when the configuration contains a placeholder, and rejected when it does not**: either mismatch returns error 7. A blank `param` counts as absent.\n\nRequires the `tracker_configure` and `tracker_set_output` rights.", "operationId": "trackerCommandExecute", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "command_id": { "type": "integer", "description": "ID of the command to run.", "minimum": 1, "examples": [ 3 ] }, "param": { "type": "string", "description": "Value that replaces the `<>` placeholder in the command configuration. Required if the configuration contains one, rejected if it does not. Up to 500 printable characters.", "maxLength": 500, "examples": [ "1" ] } }, "required": [ "tracker_id", "command_id" ] } } } }, "responses": { "200": { "description": "The command was executed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: the command or the tracker does not exist.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/command/delete": { "post": { "tags": [ "Trackers" ], "summary": "Delete a command", "description": "Remove a stored command from a tracker.\n\nRequires the `tracker_update` right.", "operationId": "trackerCommandDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "command_id": { "type": "integer", "description": "ID of the command to delete.", "minimum": 1, "examples": [ 5 ] } }, "required": [ "tracker_id", "command_id" ] } } } }, "responses": { "200": { "description": "The command was deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: the command or the tracker does not exist.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/batch_get_commands": { "post": { "tags": [ "Trackers" ], "summary": "Read commands for several trackers", "description": "Get the stored commands for the given trackers, grouped by tracker ID. Available to demo accounts.\n\nOmitting `trackers` returns commands for every tracker the account can reach. Trackers with no commands come back as empty arrays.", "operationId": "trackerBatchGetCommands", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Optional. Tracker IDs. Omit for every accessible tracker.", "items": { "type": "integer" }, "examples": [ [ 70074765, 70074766 ] ] } } } } } }, "responses": { "200": { "description": "Commands per tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "result": { "type": "object", "description": "Command lists keyed by tracker ID.", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/TrackerCommand" } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/contact/list": { "post": { "tags": [ "Trackers" ], "summary": "List contacts and their shared trackers (deprecated)", "description": "**Deprecated. Do not use.** Get the account's trackers grouped by the contacts that share them.\n\n**The path is `/contact/list`, not `/tracker/contact/list`.** It is registered at the top level, and the tracker-prefixed form returns error 111, \"Wrong handler\". Available to demo accounts.", "operationId": "contactList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": {} } } } }, "responses": { "200": { "description": "Contacts and trackers", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "contacts": { "type": "array", "description": "Established contacts, each with the trackers they share.", "items": { "$ref": "#/components/schemas/Contact" } }, "trackers": { "type": "array", "description": "Trackers belonging to the current account.", "items": { "$ref": "#/components/schemas/Tracker" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: not found in the database.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/counter/read": { "post": { "tags": [ "Trackers" ], "summary": "Read a counter", "description": "Get one of a tracker's counters, meaning the entity and its multiplier rather than its current value. Use `counter/value/get` for the value. Available to demo accounts.", "operationId": "trackerCounterRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "type": { "type": "string", "description": "Which counter to act on.", "enum": [ "odometer", "engine_hours" ], "examples": [ "odometer" ] } }, "required": [ "tracker_id", "type" ] } } } }, "responses": { "200": { "description": "The counter", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "$ref": "#/components/schemas/Counter" } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/counter/update": { "post": { "tags": [ "Trackers" ], "summary": "Update a counter", "description": "Change a counter's multiplier, and for the odometer the sensor it reads from.\n\n`sensor_id` applies **only** when `type` is `odometer`, and must be null otherwise. The sensor must be a metering sensor belonging to this same tracker; every one of those conditions is reported as error 7.\n\nRequires the `tracker_update` right.", "operationId": "trackerCounterUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "type": { "type": "string", "description": "Which counter to act on.", "enum": [ "odometer", "engine_hours" ], "examples": [ "odometer" ] }, "multiplier": { "type": "number", "description": "New multiplier for the counter.", "examples": [ 1.34 ] }, "sensor_id": { "type": [ "integer", "null" ], "description": "Sensor to use as the odometer source. Must be null unless `type` is `odometer`.", "examples": [ 123 ] } }, "required": [ "tracker_id", "type", "multiplier" ] } } } }, "responses": { "200": { "description": "The counter was updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 8: the queue service could not accept the change, retry later. Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/get_counters": { "post": { "tags": [ "Trackers" ], "summary": "Read all counter values", "description": "Get the last value of every counter on a tracker. Available to demo accounts.\n\nNote the path: this one sits at the tracker root, not under `tracker/counter`.", "operationId": "trackerGetCounters", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The counter values", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "user_time": { "type": "string", "description": "Current time in the account timezone." }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/CounterValue" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/counter/value/get": { "post": { "tags": [ "Trackers" ], "summary": "Read one counter value", "description": "Get the current value of one counter. Available to demo accounts.\n\nError 204 covers three different situations: no such tracker, no such counter, and a counter that exists but has no values yet. Use `counter/value/set` to create a counter and give it a first value.", "operationId": "trackerCounterValueGet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "type": { "type": "string", "description": "Which counter to act on.", "enum": [ "odometer", "engine_hours" ], "examples": [ "odometer" ] } }, "required": [ "tracker_id", "type" ] } } } }, "responses": { "200": { "description": "The counter value", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "number", "description": "The current value.", "examples": [ 18.9 ] } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/counter/value/list": { "post": { "tags": [ "Trackers" ], "summary": "Read one counter across several trackers", "description": "Get the current value of the same counter on many trackers. Available to demo accounts.", "operationId": "trackerCounterValueList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Tracker IDs.", "items": { "type": "integer" }, "examples": [ [ 123456, 234567 ] ] }, "type": { "type": "string", "description": "Which counter to act on.", "enum": [ "odometer", "engine_hours" ], "examples": [ "odometer" ] } }, "required": [ "trackers", "type" ] } } } }, "responses": { "200": { "description": "The counter values", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "object", "description": "Values keyed by tracker ID.", "additionalProperties": { "type": "number", "description": "The counter value for that tracker." } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: one of the counters does not exist or has no values yet. Error 217: the list names trackers that do not exist or are blocked.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/counter/value/set": { "post": { "tags": [ "Trackers" ], "summary": "Set a counter value", "description": "Set a counter's value, creating the counter if it does not exist yet. This is the call that seeds a counter so `value/get` stops answering error 204.\n\nRequires the `tracker_update` right.", "operationId": "trackerCounterValueSet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "type": { "type": "string", "description": "Which counter to act on.", "enum": [ "odometer", "engine_hours" ], "examples": [ "odometer" ] }, "value": { "type": "number", "description": "New value for the counter.", "examples": [ 233.21 ] } }, "required": [ "tracker_id", "type", "value" ] } } } }, "responses": { "200": { "description": "The value was set", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 8: the queue service could not accept the change, retry later. Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/counter/data/read": { "post": { "tags": [ "Trackers" ], "summary": "Read counter history", "description": "Get a counter's recorded values over a period. Available to demo accounts.\n\n**The period cannot exceed 31 days**, and `from` must precede `to`; both are reported as error 7 rather than as the more general error 211.", "operationId": "trackerCounterDataRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "type": { "type": "string", "description": "Which counter to act on.", "enum": [ "odometer", "engine_hours" ], "examples": [ "odometer" ] }, "from": { "type": "string", "description": "Start of the period.", "examples": [ "2021-02-25 12:21:17" ] }, "to": { "type": "string", "description": "End of the period. At most 31 days after `from`.", "examples": [ "2021-03-25 12:21:17" ] } }, "required": [ "tracker_id", "type", "from", "to" ] } } } }, "responses": { "200": { "description": "The counter history", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "description": "Recorded values, oldest first.", "items": { "type": "object", "properties": { "value": { "type": "number", "description": "The counter value at that moment.", "examples": [ 3835.52 ] }, "update_time": { "type": "string", "description": "When the value was recorded.", "examples": [ "2021-02-01 02:52:55" ] } } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no such tracker or counter. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 211: the period is longer than the platform allows.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/employee/read": { "post": { "tags": [ "Trackers" ], "summary": "Read the assigned employee", "description": "Get the employee currently assigned to a tracker, and when and where the assignment last changed. Available to demo accounts.", "operationId": "trackerEmployeeRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The assignment", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "current": { "description": "The employee currently assigned, or null.", "oneOf": [ { "$ref": "#/components/schemas/Employee" }, { "type": "null" } ] }, "last_change": { "type": [ "object", "null" ], "description": "How the assignment last changed, or null.", "properties": { "id": { "type": "integer", "description": "ID of the change record.", "format": "int64" }, "old_employee_id": { "type": [ "integer", "null" ], "description": "Deprecated. Always null.", "deprecated": true }, "new_employee_id": { "type": [ "integer", "null" ], "description": "Employee assigned to the tracker. May be null." }, "location": { "type": [ "object", "null" ], "description": "Where the change happened. May be null.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": [ "string", "null" ], "description": "Address, may be null." } } }, "changed": { "type": "string", "description": "When the change happened." }, "origin": { "type": "string", "description": "What made the change: `supervisor` for an API call, `tracker` for a hardware driver key.", "enum": [ "supervisor", "tracker" ] }, "hardware_key": { "type": [ "string", "null" ], "description": "Hardware key used to change the employee." } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/employee/assign": { "post": { "tags": [ "Trackers" ], "summary": "Assign or unassign an employee", "description": "Assign or unassign the employee (driver) on a tracker.\n\n_Requires the `app_fleet` tariff feature._\n\nWhen `new_employee_id` is provided, that employee is assigned to the tracker. When it is **omitted or `null`**, any existing assignment is cleared (unassign). Error 263 is returned when the call would produce no change, for example unassigning a tracker that already has no employee.\n\nRequires the `employee_update` right.", "operationId": "trackerEmployeeAssign", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "new_employee_id": { "type": [ "integer", "null" ], "description": "ID of the employee to assign. Omit or pass `null` to unassign the current employee from the tracker.", "minimum": 1, "examples": [ 12345 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The assignment was updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker or employee with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 263: no change needed, because the tracker already has this assignment state.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/engine_immobilizer/read": { "post": { "tags": [ "Trackers" ], "summary": "Read immobiliser state", "description": "Get whether the engine immobiliser is engaged. An immobiliser prevents the engine from starting, which is what makes hot-wiring ineffective. Available to demo accounts.", "operationId": "trackerEngineImmobilizerRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 999199 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The immobiliser state", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "enabled": { "type": "boolean", "description": "`true` when the immobiliser is engaged." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: the device does not support an immobiliser.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/engine_immobilizer/set": { "post": { "tags": [ "Trackers" ], "summary": "Engage or release the immobiliser", "description": "Change the engine immobiliser state. **The device must be online**, and this reaches the vehicle: engaging the immobiliser can prevent it from starting.\n\nRequires the `tracker_set_output` right.", "operationId": "trackerEngineImmobilizerSet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 999199 ] }, "enabled": { "type": "boolean", "description": "Whether the immobiliser should be engaged." } }, "required": [ "tracker_id", "enabled" ] } } } }, "responses": { "200": { "description": "The change was sent", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 213: the device is offline. Error 214: the device does not support this operation. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/group/list": { "post": { "tags": [ "Trackers" ], "summary": "List tracker groups", "description": "Get the account's tracker groups. Reachable from a geo link session as well as a normal one.\n\nThere is always a default unnamed group with ID `0`. It cannot be changed or deleted and **is not returned here**.", "operationId": "trackerGroupList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": {} } } } }, "responses": { "200": { "description": "The groups", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/TrackerGroup" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/group/create": { "post": { "tags": [ "Trackers" ], "summary": "Create a group", "description": "Create a new, empty tracker group. Both `title` and `colour` are required.\n\nRequires the `admin` right.", "operationId": "trackerGroupCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "title": { "type": "string", "description": "Group title.", "minLength": 1, "maxLength": 60, "examples": [ "Employees" ] }, "color": { "type": "string", "description": "Group colour in web format without a leading `#`.", "examples": [ "FF6DDC" ] } }, "required": [ "title", "color" ] } } } }, "responses": { "200": { "description": "The created group", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new group.", "examples": [ 222 ] } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/group/update": { "post": { "tags": [ "Trackers" ], "summary": "Update a group", "description": "Change a group's title and colour. **Both are required**, so this always writes both.\n\nRequires the `admin` right.", "operationId": "trackerGroupUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the group to update.", "examples": [ 167 ] }, "title": { "type": "string", "description": "Group title.", "minLength": 1, "maxLength": 60, "examples": [ "Employees" ] }, "color": { "type": "string", "description": "Group colour in web format without a leading `#`.", "examples": [ "FF6DDC" ] } }, "required": [ "id", "title", "color" ] } } } }, "responses": { "200": { "description": "The group was updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no group with this ID, or it belongs to another user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/group/delete": { "post": { "tags": [ "Trackers" ], "summary": "Delete a group", "description": "Delete a group. **Its trackers are not deleted**: they move to the default group, ID `0`.\n\nRequires the `admin` right.", "operationId": "trackerGroupDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "ID of the group to delete.", "examples": [ 167 ] } }, "required": [ "id" ] } } } }, "responses": { "200": { "description": "The group was deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no group with this ID, or it belongs to another user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/group/assign": { "post": { "tags": [ "Trackers" ], "summary": "Assign trackers to a group", "description": "Move several trackers into a group at once. Pass `0` as the group to take them out of any group.\n\nRequires the `admin` right.", "operationId": "trackerGroupAssign", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "Target group ID, or `0` to remove the trackers from any group.", "examples": [ 167 ] }, "trackers": { "type": "array", "description": "Trackers to move.", "items": { "type": "integer" }, "examples": [ [ 999199, 999919 ] ] } }, "required": [ "id", "trackers" ] } } } }, "responses": { "200": { "description": "The trackers were assigned", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no group with this ID, or it belongs to another user. Error 217: the list names trackers that do not exist or belong to another user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/led/read": { "post": { "tags": [ "Trackers" ], "summary": "Read LED state", "description": "Get whether the device's indicator LED is on. Available to demo accounts, and only for models that expose an LED switch.", "operationId": "trackerLedRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 999199 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The LED state", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "boolean", "description": "`true` when the LED is on." } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: the device does not support this operation.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/led/update": { "post": { "tags": [ "Trackers" ], "summary": "Switch the LED", "description": "Turn the device's indicator LED on or off. This reaches the hardware.", "operationId": "trackerLedUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 999199 ] }, "value": { "type": "boolean", "description": "The new LED state." } }, "required": [ "tracker_id", "value" ] } } } }, "responses": { "200": { "description": "The change was sent", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 214: the device does not support this operation.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/output/set": { "post": { "tags": [ "Trackers" ], "summary": "Switch one output", "description": "Change the state of a single digital output. **The device must be online.**\n\nSome models accept this call and others only `output/set_all`; the model's `output_control` field says which. An output reserved for the engine-block feature cannot be switched here, for safety, and answers error 214.\n\nTo rename an output rather than switch it, use `tracker/output/update`.\n\nRequires the `tracker_set_output` right.", "operationId": "trackerOutputSet", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 999199 ] }, "output": { "type": "integer", "description": "Which output to change, counting from 1.", "minimum": 1, "examples": [ 1 ] }, "enable": { "type": "boolean", "description": "Whether the output should be on." } }, "required": [ "tracker_id", "output", "enable" ] } } } }, "responses": { "200": { "description": "The change was sent", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 213: the device is offline. Error 214: the device does not support this operation. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/output/set_all": { "post": { "tags": [ "Trackers" ], "summary": "Switch all outputs", "description": "Set the state of every digital output at once. **The device must be online.**\n\nThe array must match the number of outputs the model has; a mismatch answers error 214, as does a model that does not support batch switching.\n\nRequires the `tracker_set_output` right.", "operationId": "trackerOutputSetAll", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 999199 ] }, "outputs": { "type": "array", "description": "Desired state of every output in order. `[true, true, false]` turns outputs 1 and 2 on and output 3 off.", "items": { "type": "boolean" }, "examples": [ [ true, true, false ] ] } }, "required": [ "tracker_id", "outputs" ] } } } }, "responses": { "200": { "description": "The change was sent", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 213: the device is offline. Error 214: the device does not support this operation. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/readings/list": { "post": { "tags": [ "Trackers" ], "summary": "Read all sensor values", "description": "Get the last value of every sensor, state field and counter on a tracker. This is the broadest of the reading calls: unlike `get_fuel`, `get_readings` and `get_diagnostics`, it covers CAN, OBD and fuel together and adds virtual sensors and counters. Reachable from a geo link session.\n\n**Filtering by `sensor_type` drops the state values and counters** from the response, leaving only matching sensors.", "operationId": "trackerReadingsList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 999199 ] }, "sensor_type": { "type": "string", "description": "Optional. Return only sensors of this metering or virtual type. Supplying it also omits `states` and `counters`.", "examples": [ "fuel" ] }, "include_components": { "type": "boolean", "description": "Optional. Default `true`. Set `false` to leave out the parts that make up composite sensors." } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The readings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "inputs": { "type": "array", "items": { "$ref": "#/components/schemas/SensorReading" } }, "states": { "type": "array", "items": { "$ref": "#/components/schemas/StateReading" } }, "virtual_sensors": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualSensorReading" } }, "counters": { "type": "array", "items": { "$ref": "#/components/schemas/CounterValue" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/readings/batch_list": { "post": { "tags": [ "Trackers" ], "summary": "Read all sensor values for several trackers", "description": "The many-tracker form of `readings/list`. Reachable from a geo link session.\n\nA tracker with nothing to report still appears in `result`, with empty arrays, and keys are absent rather than empty when a whole category has no data.", "operationId": "trackerReadingsBatchList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Tracker IDs.", "items": { "type": "integer" }, "examples": [ [ 999199, 991999 ] ] }, "sensor_type": { "type": "string", "description": "Optional. Return only sensors of this metering or virtual type. Supplying it also omits `states` and `counters`.", "examples": [ "fuel" ] }, "include_components": { "type": "boolean", "description": "Optional. Default `true`. Set `false` to leave out the parts that make up composite sensors." } }, "required": [ "trackers" ] } } } }, "responses": { "200": { "description": "Readings per tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "result": { "type": "object", "description": "Readings keyed by tracker ID.", "additionalProperties": { "$ref": "#/components/schemas/TrackerReadings" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 217: the list names trackers that do not exist.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/retranslator/bind": { "post": { "tags": [ "Trackers" ], "summary": "Bind a tracker to a retranslator", "description": "Start forwarding a tracker's data to a retranslator, or update an existing binding. The retranslator itself is created with the `retranslator` resource.\n\nRequires the `admin` right.", "operationId": "trackerRetranslatorBind", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 999199 ] }, "retranslator_id": { "type": "integer", "description": "ID of the retranslator.", "examples": [ 123 ] }, "fake_device_id": { "type": "string", "description": "Optional. Forward this value instead of the real device ID. Must be valid for the retranslator's protocol, which the protocol's `fake_device_id_pattern` defines.", "examples": [ "AI568T" ] } }, "required": [ "tracker_id", "retranslator_id" ] } } } }, "responses": { "200": { "description": "The binding was created", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no such tracker or retranslator. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 219: the tracker is a clone. Error 236: no tracker has the retranslation tariff feature. Error 242: `fake_device_id` is invalid for the retranslator's protocol.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/retranslator/list": { "post": { "tags": [ "Trackers" ], "summary": "List a tracker's retranslator bindings", "description": "Get the retranslators a tracker forwards data to. Available to demo accounts.", "operationId": "trackerRetranslatorList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 999199 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The bindings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/RetranslatorBinding" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/retranslator/unbind": { "post": { "tags": [ "Trackers" ], "summary": "Unbind a tracker from a retranslator", "description": "Stop forwarding a tracker's data to a retranslator.\n\nRequires the `admin` right.", "operationId": "trackerRetranslatorUnbind", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 999199 ] }, "retranslator_id": { "type": "integer", "description": "ID of the retranslator.", "examples": [ 123 ] } }, "required": [ "tracker_id", "retranslator_id" ] } } } }, "responses": { "200": { "description": "The binding was removed", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/trusted_number/list": { "post": { "tags": [ "Trackers" ], "summary": "List trusted numbers", "description": "Get the phone numbers trusted to command this tracker by SMS. Available to demo accounts.", "operationId": "trackerTrustedNumberList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 999199 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The trusted numbers", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "description": "Trusted phone numbers, in international format without a leading `+`.", "items": { "type": "string" }, "examples": [ [ "496156680000", "496156680001" ] ] } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/trusted_number/update": { "post": { "tags": [ "Trackers" ], "summary": "Replace the trusted numbers", "description": "**Replaces** the whole list of trusted numbers rather than adding to it. Send an empty array to clear it.\n\nRequires the `tracker_update` right.", "operationId": "trackerTrustedNumberUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 999199 ] }, "list": { "type": "array", "description": "The complete new list of phone numbers, 10 to 15 digits each, in international format without a leading `+`.", "items": { "type": "string", "description": "A phone number." }, "examples": [ [ "496156680001", "496156680000" ] ] } }, "required": [ "tracker_id", "list" ] } } } }, "responses": { "200": { "description": "The list was replaced", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/rule/list": { "post": { "tags": [ "Trackers" ], "summary": "List rules", "description": "Get the rules bound to one tracker, or every rule on the account when `tracker_id` is omitted. Available to demo accounts.", "operationId": "trackerRuleList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "Optional. Return only rules bound to this tracker. Omit for every rule on the account.", "examples": [ 999199 ] } } } } } }, "responses": { "200": { "description": "The rules", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Rule" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/rule/create": { "post": { "tags": [ "Trackers" ], "summary": "Create a rule", "description": "Create a rule and its schedule intervals.\n\n**The rule goes in a `rule` object**, not as top-level fields. Sending them flat is rejected with error 7.\n\nGeofence binding differs by type: `zone_ids` is not allowed for the `offline` type, and cannot be empty for `route` or `inoutzone`. The rule types page documents `param`, `extended_params`, and the meaning of `primary_text` and `secondary_text` for each type.\n\nRequires the `tracker_rule_update` right.", "operationId": "trackerRuleCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "rule": { "allOf": [ { "$ref": "#/components/schemas/Rule" } ], "description": "The rule to create, without `id`. Send geofences as `zone_ids`, an array." } }, "required": [ "rule" ] } } } }, "responses": { "200": { "description": "The created rule", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new rule.", "examples": [ 123 ] } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: an associated geofence does not exist.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/rule/update": { "post": { "tags": [ "Trackers" ], "summary": "Update a rule", "description": "Replace a rule and its schedule intervals.\n\n**The rule goes in a `rule` object** and must carry its `id`. As with creation, sending the fields flat is rejected.\n\nRequires the `tracker_rule_update` right.", "operationId": "trackerRuleUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "rule": { "allOf": [ { "$ref": "#/components/schemas/Rule" } ], "description": "The full rule, including `id`. Send geofences as `zone_ids`, an array." } }, "required": [ "rule" ] } } } }, "responses": { "200": { "description": "The rule was updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no rule with this ID, or it belongs to another user. Error 204: a newly associated geofence does not exist.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/rule/delete": { "post": { "tags": [ "Trackers" ], "summary": "Delete a rule", "description": "Delete a rule and everything related to it, including its schedule intervals.\n\nRequires the `tracker_rule_update` right.", "operationId": "trackerRuleDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "rule_id": { "type": "integer", "description": "ID of the rule.", "examples": [ 123 ] } }, "required": [ "rule_id" ] } } } }, "responses": { "200": { "description": "The rule was deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no rule with this ID, or it belongs to another user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/rule/bind": { "post": { "tags": [ "Trackers" ], "summary": "Bind a rule to trackers", "description": "Apply an existing rule to more trackers.\n\nTrackers that do not exist, belong to someone else, or are deleted are **skipped silently** rather than failing the call, so a success does not mean every ID in the list was bound.\n\nRequires the `tracker_rule_update` right.", "operationId": "trackerRuleBind", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "rule_id": { "type": "integer", "description": "ID of the rule.", "examples": [ 123 ] }, "trackers": { "type": "array", "description": "Trackers to bind. Unknown or inaccessible IDs are ignored without error.", "items": { "type": "integer" }, "examples": [ [ 265489 ] ] } }, "required": [ "rule_id", "trackers" ] } } } }, "responses": { "200": { "description": "The rule was bound", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no rule with this ID, or it belongs to another user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/rule/unbind": { "post": { "tags": [ "Trackers" ], "summary": "Unbind a rule from trackers", "description": "Stop applying a rule to the given trackers. As with binding, unknown or inaccessible IDs are ignored without error.\n\nRequires the `tracker_rule_update` right.", "operationId": "trackerRuleUnbind", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "rule_id": { "type": "integer", "description": "ID of the rule.", "examples": [ 123 ] }, "trackers": { "type": "array", "description": "Trackers to unbind. Unknown or inaccessible IDs are ignored without error.", "items": { "type": "integer" }, "examples": [ [ 265489 ] ] } }, "required": [ "rule_id", "trackers" ] } } } }, "responses": { "200": { "description": "The rule was unbound", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no rule with this ID, or it belongs to another user.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/sensor/list": { "post": { "tags": [ "Trackers" ], "summary": "List a tracker's sensors", "description": "Get every sensor configured on one tracker. Available to demo accounts, and reachable from a fuel-server session as well as a normal one.", "operationId": "trackerSensorList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The sensors", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/Sensor" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/sensor/batch_list": { "post": { "tags": [ "Trackers" ], "summary": "List sensors for several trackers", "description": "The many-tracker form of `sensor/list`. Available to demo accounts.", "operationId": "trackerSensorBatchList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "trackers": { "type": "array", "description": "Tracker IDs. At most 500, and the limit may change.", "items": { "type": "integer" }, "maxItems": 500, "examples": [ [ 11, 12 ] ] } }, "required": [ "trackers" ] } } } }, "responses": { "200": { "description": "Sensors per tracker", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "result": { "type": "object", "description": "Sensor lists keyed by tracker ID.", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/Sensor" } } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 217: one of the listed trackers does not exist or is blocked. Error 221: too many IDs were passed.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/sensor/create": { "post": { "tags": [ "Trackers" ], "summary": "Create a sensor", "description": "Add a sensor to a tracker.\n\nA discrete input number, or a metering sensor's input name, can only be used once per tracker; reusing one is error 232. The number of sensors sharing a `sensor_type` is also capped, reported as error 270.\n\nRequires the `tracker_update` right.", "operationId": "trackerSensorCreate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "sensor": { "allOf": [ { "$ref": "#/components/schemas/Sensor" } ], "description": "The sensor to create, without `id`." } }, "required": [ "tracker_id", "sensor" ] } } } }, "responses": { "200": { "description": "The created sensor", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "id": { "type": "integer", "description": "ID of the new sensor.", "examples": [ 937 ] } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 219: the tracker is a clone. Error 232: that input number or input name is already used on this tracker. Error 270: too many sensors of this type.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/sensor/update": { "post": { "tags": [ "Trackers" ], "summary": "Update a sensor", "description": "Replace a sensor's configuration. Send the full object including its `id`.\n\nRequires the `tracker_update` right.", "operationId": "trackerSensorUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "sensor": { "allOf": [ { "$ref": "#/components/schemas/Sensor" } ], "description": "The full sensor object, including `id`." } }, "required": [ "tracker_id", "sensor" ] } } } }, "responses": { "200": { "description": "The sensor was updated", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: the sensor does not exist or belongs to another user. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 219: the tracker is a clone. Error 232: that input number or input name is already used on this tracker.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/sensor/delete": { "post": { "tags": [ "Trackers" ], "summary": "Delete a sensor", "description": "Remove a sensor from a tracker.\n\nRequires the `tracker_update` right.", "operationId": "trackerSensorDelete", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "sensor_id": { "type": "integer", "description": "Sensor ID.", "minimum": 1, "examples": [ 234567 ] } }, "required": [ "tracker_id", "sensor_id" ] } } } }, "responses": { "200": { "description": "The sensor was deleted", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: the sensor does not exist or belongs to another user. Error 208: the tracker exists but is blocked, normally because its tariff ended. Error 219: the tracker is a clone.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/sensor/batch_copy": { "post": { "tags": [ "Trackers" ], "summary": "Copy sensors between trackers", "description": "Copy every sensor from one tracker onto others.\n\n**This deletes the target trackers' existing sensors first, and their historical sensor data can be lost with them.** There is no merge mode and no undo. All trackers involved must share the same model, reported as error 272.\n\nRequires the `tracker_update` right.", "operationId": "trackerSensorBatchCopy", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "base_tracker_id": { "type": "integer", "description": "Tracker to copy the sensors from.", "examples": [ 123456 ] }, "trackers": { "type": "array", "description": "Trackers to copy onto. **Their existing sensors are deleted.**", "items": { "type": "integer" }, "examples": [ [ 12345, 54321 ] ] } }, "required": [ "base_tracker_id", "trackers" ] } } } }, "responses": { "200": { "description": "The sensors were copied", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 272: the base tracker and one of the targets have different models.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/sensor/data/read": { "post": { "tags": [ "Trackers" ], "summary": "Read sensor history", "description": "Get a sensor's recorded readings over a period. Available to demo accounts, and reachable from a fuel-server session.\n\n**Metering and virtual sensors only**; a discrete input answers error 228. The period is capped by the platform's report time span, thirty days by default.", "operationId": "trackerSensorDataRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "sensor_id": { "type": "integer", "description": "Sensor ID.", "minimum": 1, "examples": [ 234567 ] }, "from": { "type": "string", "description": "Start of the period.", "examples": [ "2022-02-28 00:00:00" ] }, "to": { "type": "string", "description": "End of the period. Must be after `from`.", "examples": [ "2022-03-28 23:59:00" ] }, "raw_data": { "type": "boolean", "description": "Optional. Default `false`. Return values without calibration or multiplication applied. Affects metering sensors only." } }, "required": [ "tracker_id", "sensor_id", "from", "to" ] } } } }, "responses": { "200": { "description": "The readings", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/SensorDataPoint" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 211: the period is longer than the platform allows. Error 228: the sensor is neither metering nor virtual.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/sensor/calibration_data/read": { "post": { "tags": [ "Trackers" ], "summary": "Read calibration data", "description": "Get a sensor's calibration curve. Available to demo accounts, and reachable from a fuel-server session.", "operationId": "trackerSensorCalibrationDataRead", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "sensor_id": { "type": "integer", "description": "Sensor ID.", "minimum": 1, "examples": [ 234567 ] } }, "required": [ "tracker_id", "sensor_id" ] } } } }, "responses": { "200": { "description": "The calibration curve", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "value": { "type": "array", "items": { "$ref": "#/components/schemas/CalibrationPoint" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 228: the sensor does not support calibration.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/sensor/calibration_data/update": { "post": { "tags": [ "Trackers" ], "summary": "Replace calibration data", "description": "**Replaces** a sensor's whole calibration curve rather than adding points to it.\n\nRequires the `tracker_update` right.", "operationId": "trackerSensorCalibrationDataUpdate", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "sensor_id": { "type": "integer", "description": "Sensor ID.", "minimum": 1, "examples": [ 234567 ] }, "data": { "type": "array", "description": "The complete new curve.", "items": { "$ref": "#/components/schemas/CalibrationPoint" }, "examples": [ [ { "in": 0, "out": 0 }, { "in": 0.7, "out": 60 } ] ] } }, "required": [ "tracker_id", "sensor_id", "data" ] } } } }, "responses": { "200": { "description": "The curve was replaced", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 219: the tracker is a clone. Error 228: the sensor does not support calibration.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/sensor/calibration_data/upload_omnicomm": { "post": { "tags": [ "Trackers" ], "summary": "Upload an Omnicomm calibration file", "description": "Replace a sensor's calibration curve from an Omnicomm LLS monitor XML configuration file. **Multipart request**, with the file in a part named `file`.\n\nWhen the file describes several sensors, `sensor_id` selects which one to take.\n\nRequires the `tracker_update` right.", "operationId": "trackerSensorCalibrationDataUploadOmnicomm", "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] }, "sensor_id": { "type": "integer", "description": "Sensor ID.", "minimum": 1, "examples": [ 234567 ] }, "file": { "type": "string", "description": "The LLS monitor XML file.", "format": "binary" } }, "required": [ "tracker_id", "sensor_id", "file" ] } } } }, "responses": { "200": { "description": "The curve was replaced", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 201: no tracker with this ID belongs to the account. Error 219: the tracker is a clone. Error 228: the sensor does not support calibration. Error 233: no file part was sent. Error 234: the file is not a valid LLS monitor XML file. Error 235: the file holds no calibration data for that sensor number.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/sensor/input_name/list": { "post": { "tags": [ "Trackers" ], "summary": "List all sensor inputs and state fields", "description": "Get every sensor input and state field the platform knows, with descriptions translated into the account's language. Available to demo accounts, and takes no parameters. Reachable from a geo link session as well as a normal one.\n\nThis is the authoritative source for the `input_name` values a sensor can use, and for the state field names that appear in `get_diagnostics` and `readings/list`. Where a state field has a known value set, the value type and the translated value descriptions come back too.", "operationId": "trackerSensorInputNameList", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": {} } } } }, "responses": { "200": { "description": "The inputs and state fields", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "list": { "type": "array", "items": { "$ref": "#/components/schemas/SensorInputName" } } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/command/unconfirmed/count": { "post": { "tags": [ "Trackers" ], "summary": "Count queued SMS commands", "description": "Get how many SMS commands are still waiting in a tracker's queue. Available to demo accounts.", "operationId": "trackerCommandUnconfirmedCount", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The queue length", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] }, "count": { "type": "integer", "description": "Unconfirmed commands waiting in the queue.", "examples": [ 0 ] } } } } } }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } }, "/tracker/command/unconfirmed/reset": { "post": { "tags": [ "Trackers" ], "summary": "Clear the SMS command queue", "description": "Discard every pending SMS command for a tracker. The commands are dropped, not sent, so anything the device was waiting for will never arrive.\n\nRequires the `tracker_update` right.", "operationId": "trackerCommandUnconfirmedReset", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked.", "examples": [ 123456 ] } }, "required": [ "tracker_id" ] } } } }, "responses": { "200": { "description": "The queue was cleared", "$ref": "#/components/responses/OK" }, "400": { "description": "Bad request. The response body carries the API-level error code in `status.code`. Validation failures also include an `errors` array naming each offending parameter.", "$ref": "#/components/responses/ResponseError" }, "403": { "description": "Error 204: no tracker with this ID belongs to the account. Error 208: the tracker exists but is blocked, normally because its tariff ended.", "$ref": "#/components/responses/ResponseError" }, "default": { "$ref": "#/components/responses/ResponseError" } } } } }, "components": { "securitySchemes": { "api_key": { "type": "apiKey", "description": "Either a user session hash or an API key, with the \"NVX \" prefix, for example \"NVX 22eac1c27af4be7b9d04da2ce1af111b\". Obtain a session hash from the userAuth operation, or create an API key from the API keys operations. A small number of operations accept a session hash only and reject an API key with error code 4; each says so in its description.", "name": "Authorization", "in": "header" } }, "responses": { "OK": { "description": "Successful response", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "`true` if request finished successfully.", "readOnly": true, "examples": [ true ] } } } } } }, "ResponseError": { "description": "Error response object", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { "type": "boolean", "description": "Always false.", "examples": [ false ] }, "status": { "type": "object", "description": "Error status. Present only when an error occurred.", "properties": { "code": { "type": "integer", "description": "An error code in this API, not an HTTP code.", "examples": [ 201 ] }, "description": { "type": "string", "description": "An error description.", "examples": [ "Not found in database" ] } } }, "errors": { "type": "array", "description": "Per-parameter detail, returned with validation failures such as error code 7. A parameter name here may be an internal field name rather than the documented parameter name.", "items": { "type": "object", "properties": { "parameter": { "type": "string", "description": "Name of the parameter that failed validation.", "examples": [ "limit" ] }, "error": { "type": "string", "description": "What was wrong with it.", "examples": [ "must not be null" ] } } } } } } } } } }, "schemas": { "UserSettings": { "type": "object", "description": "A user's locale, display, and provider preferences. Filtered by the dealer's GIS package before being returned, so a field may be absent on a restricted package.", "properties": { "locale": { "type": "string", "description": "Locale code, for example `en`." }, "time_zone": { "type": "string", "description": "ISO timezone ID, for example `Europe/Budapest`." }, "date_format": { "type": [ "string", "null" ], "description": "Optional. Date representation: `ddMMyyyy_dots` is 01.12.2021, `ddMMyyyy_slashes` is 01/12/2021, `MMddyyyy_hyphens` is 12-01-2021, `yyyyMMdd_hyphens` is 2021-12-01, `dMMMy` is 1 Dec 2021, `dMMMMy` is 1 December 2021.", "enum": [ "ddMMyyyy_dots", "ddMMyyyy_slashes", "MMddyyyy_hyphens", "yyyyMMdd_hyphens", "dMMMy", "dMMMMy" ] }, "hour_mode": { "type": [ "string", "null" ], "description": "Optional. Time representation: `TWENTY_FOUR_HOURS` is a 24-hour clock such as 17:45, `TWELVE_HOURS` is a 12-hour clock such as 5:45 PM.", "enum": [ "TWELVE_HOURS", "TWENTY_FOUR_HOURS" ] }, "measurement_system": { "type": "string", "description": "Measurement system.", "enum": [ "metric", "imperial", "us", "metric_gal_us", "nautical" ] }, "geocoder": { "type": [ "string", "null" ], "description": "Preferred geocoder type.", "enum": [ "google", "yandex", "progorod", "osm", "locationiq" ] }, "route_provider": { "type": [ "string", "null" ], "description": "Preferred route finding provider.", "enum": [ "google", "progorod", "osrm" ] }, "translit": { "type": "boolean", "description": "`true` if SMS notifications are transliterated." } } }, "FileStorageSettings": { "type": "object", "description": "How uploaded files are handled.", "properties": { "auto_overwrite": { "type": "boolean", "description": "Whether uploading a file with an existing name overwrites it." } } }, "BalanceAlertSettings": { "type": "object", "description": "Where low-balance alerts are sent.", "properties": { "emails": { "type": "array", "description": "Email addresses that receive balance alerts.", "items": { "type": "string" } } } }, "TariffRestrictions": { "type": "object", "description": "Limits imposed by the user's tariff.", "properties": { "allowed_maps": { "type": "array", "description": "Map types the tariff permits.", "items": { "type": "string" } } } }, "UserInfo": { "type": "object", "description": "The current user. Address and legal fields are present but empty when never set.", "properties": { "id": { "type": "integer", "description": "User identifier." }, "login": { "type": "string", "description": "User's login, usually an email address." }, "title": { "type": "string", "description": "Display name." }, "first_name": { "type": "string", "description": "Contact person first name." }, "middle_name": { "type": "string", "description": "Contact person middle name." }, "last_name": { "type": "string", "description": "Contact person last name." }, "legal_name": { "type": "string", "description": "Optional. A juridical name." }, "phone": { "type": "string", "description": "Contact phone." }, "creation_date": { "type": "string", "description": "When the account was created." }, "balance": { "type": "number", "description": "Current account balance. A decimal, returned with four fractional digits." }, "bonus": { "type": "number", "description": "Current bonus balance. A decimal, returned with four fractional digits." }, "locale": { "type": "string", "description": "Locale code." }, "demo": { "type": "boolean", "description": "Whether this is a demo account." }, "verified": { "type": "boolean", "description": "Whether the account email is verified." }, "phone_verified": { "type": "boolean", "description": "Whether the account phone is verified." }, "legal_type": { "type": "string", "description": "Legal type of the user.", "enum": [ "legal_entity", "sole_trader", "individual" ] }, "default_geocoder": { "type": [ "string", "null" ], "description": "Preferred geocoder." }, "route_provider": { "type": [ "string", "null" ], "description": "Preferred route provider." }, "time_zone": { "type": "string", "description": "ISO timezone ID." }, "measurement_system": { "type": "string", "description": "Measurement system." }, "date_format": { "type": [ "string", "null" ], "description": "Date display format." }, "hour_mode": { "type": [ "string", "null" ], "description": "Clock format." }, "default_security_group_id": { "type": [ "integer", "null" ], "description": "Identifier of the security group new sub-users join by default." }, "tin": { "type": "string", "description": "Taxpayer identification number." }, "iec": { "type": "string", "description": "Industrial Enterprises Classifier, also known as KPP. Used in Russia." }, "post_country": { "type": "string", "description": "Country part of the post address." }, "post_region": { "type": "string", "description": "Region part of the post address." }, "post_index": { "type": "string", "description": "Index part of the post address." }, "post_city": { "type": "string", "description": "City part of the post address." }, "post_street_address": { "type": "string", "description": "Street part of the post address." }, "registered_country": { "type": "string", "description": "Country part of the registered address." }, "registered_region": { "type": "string", "description": "Region part of the registered address." }, "registered_index": { "type": "string", "description": "Index part of the registered address." }, "registered_city": { "type": "string", "description": "City part of the registered address." }, "registered_street_address": { "type": "string", "description": "Street part of the registered address." } } }, "PaasSettings": { "type": "object", "description": "Settings of the platform the user belongs to: branding, maps, providers, password policy, and registration behaviour. The same structure the Admin Panel API reads and writes through its service settings operations, and documented in full there.", "additionalProperties": true, "properties": { "domain": { "type": "string", "description": "Platform domain." }, "service_title": { "type": "string", "description": "Platform display name." }, "locale": { "type": "string", "description": "Default locale." }, "currency": { "type": "string", "description": "Currency code." }, "maps": { "type": "array", "description": "Map types the platform offers.", "items": { "type": "string" } }, "has_https": { "type": "boolean", "description": "Whether the platform serves HTTPS." }, "allow_registration": { "type": "boolean", "description": "Whether self-registration is enabled." }, "gis_package": { "type": "string", "description": "Active GIS package, which determines which user settings fields are returned." }, "password_policy": { "type": "object", "description": "Rules a new password must satisfy.", "properties": { "min_length": { "type": "integer", "description": "Minimum length." }, "max_length": { "type": "integer", "description": "Maximum length." }, "special_chars": { "type": "string", "description": "Characters that count as special." }, "min_special_chars_count": { "type": "integer", "description": "Minimum number of special characters." }, "min_digits_count": { "type": "integer", "description": "Minimum number of digits." }, "min_uppercase_count": { "type": "integer", "description": "Minimum number of uppercase letters." }, "min_lowercase_count": { "type": "integer", "description": "Minimum number of lowercase letters." } } } } }, "UserMenu": { "type": "object", "description": "The current user's menu. Each section is a list of menu items, described on the Admin Panel menu preset page.", "properties": { "title": { "type": [ "string", "null" ], "description": "Preset title, or a localisation key such as `menu-editor.default-preset`." }, "account": { "type": "array", "description": "Menu items for account management.", "items": { "type": "object", "additionalProperties": true } }, "main": { "type": "array", "description": "Menu items for the main navigation.", "items": { "type": "object", "additionalProperties": true } }, "applications": { "type": "array", "description": "Menu items for applications.", "items": { "type": "object", "additionalProperties": true } }, "footer": { "type": "object", "description": "Footer group.", "properties": { "title": { "type": [ "string", "null" ], "description": "Footer title." }, "items": { "type": "array", "description": "Menu items in the footer.", "items": { "type": "object", "additionalProperties": true } } } } } }, "UserApplication": { "type": "object", "description": "A custom application embedded into the platform interface and reachable from the side menu.\n\n**One `authorization` object, three shapes, selected by `type`.** `none` opens the URL with no credential, `user_session` passes the current platform session, and `api_key` passes a fixed key and is the only shape that carries `hash`.", "properties": { "id": { "type": "integer", "description": "Application identifier. Absent when creating, required when updating." }, "label": { "type": "string", "description": "Name shown in the side menu. Not blank, 1 to 50 characters, trimmed before it is stored.", "minLength": 1, "maxLength": 50 }, "url_template": { "type": "string", "description": "URL the application opens at. May carry placeholders that the interface substitutes when a user opens it: `{locale_code}`, `{user_identifier}`, and `{timezone}`. 4 to 1000 characters, trimmed before it is stored.", "minLength": 4, "maxLength": 1000 }, "enabled": { "type": "boolean", "description": "Whether the application appears in the side menu. Ignored on create, where a new application is always enabled. Defaults to `true` on update." }, "authorization": { "type": "object", "description": "How the application is authorised when its URL is opened. Defaults to `user_session`.", "properties": { "type": { "type": "string", "description": "Which of the three shapes this is, and therefore whether `hash` applies.", "enum": [ "none", "user_session", "api_key" ] }, "hash": { "type": "string", "description": "The API key to pass. **`api_key` only, and required for it.**" } } }, "display_method": { "type": "string", "description": "How the application opens: `new_tab` in a new browser tab, `embedded` inside the platform interface. Defaults to `new_tab`.", "enum": [ "new_tab", "embedded" ] }, "icon": { "type": "string", "description": "Icon name shown beside the label in the side menu, for example `dashboard` or `camera`. Defaults to `extension`. Not checked against a list of names, and **not length-checked either, while the column holds 50 characters**." }, "description": { "type": "string", "description": "Longer plain-text description shown to users. Up to 1000 characters, trimmed before it is stored. Defaults to empty.", "maxLength": 1000 } }, "required": [ "label", "url_template" ] }, "AuditLogRecord": { "type": "object", "description": "One audit log entry. Fields that do not apply to the recorded action are absent.", "properties": { "id": { "type": "integer", "description": "Record identifier." }, "user_id": { "type": "integer", "description": "Master user the record belongs to." }, "subuser_id": { "type": "integer", "description": "Sub-user who performed the action." }, "entry_category": { "type": "string", "description": "Category of the entity acted on, for example `user` or `custom_field`." }, "entry_id": { "type": [ "integer", "null" ], "description": "Identifier of the entity acted on, when the action targets one." }, "action": { "type": "string", "description": "Action performed, for example `create` or `checkin`." }, "payload": { "type": [ "object", "null" ], "description": "Additional information about the action. Typically the fields that changed. Absent or null for actions that carry none.", "additionalProperties": true }, "host": { "type": "string", "description": "Source IP address of the request, IPv4 or IPv6." }, "user_agent": { "type": "string", "description": "User agent of the client that made the request." }, "action_date": { "type": "string", "description": "When the action occurred." } } }, "Vehicle": { "type": "object", "description": "A vehicle in the fleet.", "properties": { "id": { "type": "integer", "description": "ID of the vehicle. Omit when creating." }, "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as object_id. Must belong to the authorized user and not be blocked." }, "tracker_label": { "type": "string", "description": "Optional. Tracker's label." }, "label": { "type": "string", "description": "Vehicle's label." }, "icon_color": { "type": [ "string", "null" ], "description": "Colour of the vehicle icon, as six hex digits without a leading `#`.", "examples": [ "1E96DC" ] }, "status_id": { "type": [ "integer", "null" ], "description": "ID of the vehicle status currently assigned, or null when none is. Set it with `vehicle/status/*`." }, "trailer_reg_number": { "type": "string", "description": "Registration number of the attached trailer. Empty string when there is none." }, "max_speed": { "type": [ "integer", "null" ], "description": "Maximum speed of the vehicle." }, "model": { "type": "string", "description": "Vehicle's model." }, "type": { "type": "string", "description": "Vehicle's type.", "enum": [ "truck", "car", "bus", "special" ] }, "subtype": { "type": [ "string", "null" ], "description": "Optional. Depends on `type`; null means undefined. For `car`: sedan, universal, hatchback, liftback, limousine, pickup, minivan, coupe, coupe4d, muscle, convertible, phaeton, lando, crossover, roadster, suv. For `truck`: tipper, board, covered, awning, mixer, tanker, refrigerator, transporter, container, tractor. For `bus`: city, shuttle, platform, school, intercity, sightseeing. For `special`: mobile_crane, racing, buggy, ambulance, firefighter, hearse, shop, harvester, snowplow, tractor, grader, excavator, bulldozer, armored, amphibian, boat." }, "garage_id": { "type": [ "integer", "null" ], "description": "ID of a garage." }, "garage_organization_name": { "type": "string", "description": "Optional. Garage organization name." }, "trailer": { "type": "string", "description": "Optional. Information about a trailer." }, "manufacture_year": { "type": [ "integer", "null" ], "description": "Optional. Manufacture year." }, "color": { "type": "string", "description": "Optional. Colour of the vehicle, as free text rather than RGB." }, "additional_info": { "type": "string", "description": "Optional. Additional info." }, "reg_number": { "type": "string", "description": "Registration number or licence plate." }, "vin": { "type": "string", "description": "VIN of the vehicle." }, "chassis_number": { "type": "string", "description": "Chassis number." }, "frame_number": { "type": "string", "description": "Optional. Frame number." }, "payload_weight": { "type": [ "integer", "null" ], "description": "Payload weight in kilograms." }, "payload_height": { "type": [ "number", "null" ], "description": "Payload height in millimetres." }, "payload_length": { "type": [ "number", "null" ], "description": "Payload length in millimetres." }, "payload_width": { "type": [ "number", "null" ], "description": "Payload width in millimetres." }, "passengers": { "type": [ "integer", "null" ], "description": "Maximum number of passengers." }, "gross_weight": { "type": [ "integer", "null" ], "description": "Optional. Gross weight in kilograms." }, "fuel_type": { "type": [ "string", "null" ], "description": "Fuel type.", "enum": [ "petrol", "diesel", "gas" ] }, "fuel_grade": { "type": "string", "description": "Grade of fuel used." }, "norm_avg_fuel_consumption": { "type": [ "number", "null" ], "description": "Normal average fuel consumption, litres per 100 km." }, "fuel_tank_volume": { "type": [ "integer", "null" ], "description": "Fuel tank capacity in litres." }, "fuel_cost": { "type": [ "number", "null" ], "description": "Optional. Cost of fuel per litre." }, "wheel_arrangement": { "type": "string", "description": "Wheel arrangement, for example `4x2`." }, "tyre_size": { "type": "string", "description": "Tyre size." }, "tyres_number": { "type": [ "integer", "null" ], "description": "Number of tyres." }, "liability_insurance_policy_number": { "type": "string", "description": "Liability insurance policy number." }, "liability_insurance_valid_till": { "type": [ "string", "null" ], "description": "Date the liability insurance is valid until." }, "free_insurance_policy_number": { "type": "string", "description": "Free insurance policy number." }, "free_insurance_valid_till": { "type": [ "string", "null" ], "description": "Date the free insurance is valid until." }, "icon_id": { "type": [ "integer", "null" ], "description": "ID of the icon. **Read-only here**: change it through `vehicleAvatarAssign`." }, "avatar_file_name": { "type": [ "string", "null" ], "description": "File name of the uploaded avatar." }, "tags": { "type": "array", "description": "IDs of tags applied to the vehicle.", "items": { "type": "integer" } } } }, "CheckedVehicle": { "type": "object", "description": "A vehicle parsed from a batch, with the same fields as a vehicle plus any per-row problems found while checking it.", "properties": { "id": { "type": "integer", "description": "ID of the vehicle. Omit when creating." }, "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as object_id. Must belong to the authorized user and not be blocked." }, "tracker_label": { "type": "string", "description": "Optional. Tracker's label." }, "label": { "type": "string", "description": "Vehicle's label." }, "icon_color": { "type": [ "string", "null" ], "description": "Colour of the vehicle icon, as six hex digits without a leading `#`.", "examples": [ "1E96DC" ] }, "status_id": { "type": [ "integer", "null" ], "description": "ID of the vehicle status currently assigned, or null when none is. Set it with `vehicle/status/*`." }, "trailer_reg_number": { "type": "string", "description": "Registration number of the attached trailer. Empty string when there is none." }, "max_speed": { "type": [ "integer", "null" ], "description": "Maximum speed of the vehicle." }, "model": { "type": "string", "description": "Vehicle's model." }, "type": { "type": "string", "description": "Vehicle's type.", "enum": [ "truck", "car", "bus", "special" ] }, "subtype": { "type": [ "string", "null" ], "description": "Optional. Depends on `type`; null means undefined. For `car`: sedan, universal, hatchback, liftback, limousine, pickup, minivan, coupe, coupe4d, muscle, convertible, phaeton, lando, crossover, roadster, suv. For `truck`: tipper, board, covered, awning, mixer, tanker, refrigerator, transporter, container, tractor. For `bus`: city, shuttle, platform, school, intercity, sightseeing. For `special`: mobile_crane, racing, buggy, ambulance, firefighter, hearse, shop, harvester, snowplow, tractor, grader, excavator, bulldozer, armored, amphibian, boat." }, "garage_id": { "type": [ "integer", "null" ], "description": "ID of a garage." }, "garage_organization_name": { "type": "string", "description": "Optional. Garage organization name." }, "trailer": { "type": "string", "description": "Optional. Information about a trailer." }, "manufacture_year": { "type": [ "integer", "null" ], "description": "Optional. Manufacture year." }, "color": { "type": "string", "description": "Optional. Colour of the vehicle, as free text rather than RGB." }, "additional_info": { "type": "string", "description": "Optional. Additional info." }, "reg_number": { "type": "string", "description": "Registration number or licence plate." }, "vin": { "type": "string", "description": "VIN of the vehicle." }, "chassis_number": { "type": "string", "description": "Chassis number." }, "frame_number": { "type": "string", "description": "Optional. Frame number." }, "payload_weight": { "type": [ "integer", "null" ], "description": "Payload weight in kilograms." }, "payload_height": { "type": [ "number", "null" ], "description": "Payload height in millimetres." }, "payload_length": { "type": [ "number", "null" ], "description": "Payload length in millimetres." }, "payload_width": { "type": [ "number", "null" ], "description": "Payload width in millimetres." }, "passengers": { "type": [ "integer", "null" ], "description": "Maximum number of passengers." }, "gross_weight": { "type": [ "integer", "null" ], "description": "Optional. Gross weight in kilograms." }, "fuel_type": { "type": [ "string", "null" ], "description": "Fuel type.", "enum": [ "petrol", "diesel", "gas" ] }, "fuel_grade": { "type": "string", "description": "Grade of fuel used." }, "norm_avg_fuel_consumption": { "type": [ "number", "null" ], "description": "Normal average fuel consumption, litres per 100 km." }, "fuel_tank_volume": { "type": [ "integer", "null" ], "description": "Fuel tank capacity in litres." }, "fuel_cost": { "type": [ "number", "null" ], "description": "Optional. Cost of fuel per litre." }, "wheel_arrangement": { "type": "string", "description": "Wheel arrangement, for example `4x2`." }, "tyre_size": { "type": "string", "description": "Tyre size." }, "tyres_number": { "type": [ "integer", "null" ], "description": "Number of tyres." }, "liability_insurance_policy_number": { "type": "string", "description": "Liability insurance policy number." }, "liability_insurance_valid_till": { "type": [ "string", "null" ], "description": "Date the liability insurance is valid until." }, "free_insurance_policy_number": { "type": "string", "description": "Free insurance policy number." }, "free_insurance_valid_till": { "type": [ "string", "null" ], "description": "Date the free insurance is valid until." }, "icon_id": { "type": [ "integer", "null" ], "description": "ID of the icon. **Read-only here**: change it through `vehicleAvatarAssign`." }, "avatar_file_name": { "type": [ "string", "null" ], "description": "File name of the uploaded avatar." }, "tags": { "type": "array", "description": "IDs of tags applied to the vehicle.", "items": { "type": "integer" } } }, "additionalProperties": true }, "VehicleStatusListing": { "type": "object", "description": "One entry in the vehicle status list.", "properties": { "id": { "type": "integer", "description": "ID of the status." }, "order": { "type": "integer", "description": "Position of the status. **Ignored on update**, where position comes from the array order." }, "label": { "type": "string", "description": "Status name or description." }, "color": { "type": "string", "description": "RGB colour." } } }, "ServiceTaskConditions": { "type": "object", "description": "When the service work falls due. **At least one of `mileage`, `date`, or `engine_hours` must be given.**", "properties": { "mileage": { "type": "object", "description": "Fall due at a mileage.", "properties": { "limit": { "type": "integer", "description": "Mileage limit in kilometres." }, "notification_interval": { "type": "integer", "description": "How far before the limit to start notifying." }, "repeat_interval": { "type": "integer", "description": "Interval for the repeated task, when `repeat` is true." } } }, "date": { "type": "object", "description": "Fall due on a date.", "properties": { "end": { "type": "string", "description": "Date and time the work falls due." }, "notification_interval": { "type": "integer", "description": "How many days before the date to start notifying." }, "repeat_interval": { "type": "integer", "description": "Interval for the repeated task, when `repeat` is true." } } }, "engine_hours": { "type": "object", "description": "Fall due at an engine-hours reading.", "properties": { "limit": { "type": "integer", "description": "Engine-hours limit." }, "notification_interval": { "type": "integer", "description": "How far before the limit to start notifying." }, "repeat_interval": { "type": "integer", "description": "Interval for the repeated task, when `repeat` is true." } } } } }, "ServiceTaskNotifications": { "type": "object", "description": "Where notifications about this service work are sent.", "properties": { "sms_phones": { "type": "array", "description": "Phone numbers to notify by SMS.", "items": { "type": "string" } }, "emails": { "type": "array", "description": "Email addresses to notify.", "items": { "type": "string" } }, "push_enabled": { "type": "boolean", "description": "Whether push notifications are sent." } } }, "ServiceTask": { "type": "object", "description": "A service work scheduled against a vehicle.", "properties": { "id": { "type": "integer", "description": "ID of the service work. Omit when creating." }, "vehicle_id": { "type": "integer", "description": "ID of the associated vehicle." }, "vehicle_label": { "type": "string", "description": "Label of the associated vehicle." }, "description": { "type": "string", "description": "Name of the service work. Up to 255 characters.", "maxLength": 255 }, "comment": { "type": "string", "description": "Comment. Up to 255 characters.", "maxLength": 255 }, "status": { "type": "string", "description": "Current status. `created` is the initial state, `notified` means a condition passed its notification limit, `expired` means a condition was exceeded, `done` means the user marked it complete.", "enum": [ "created", "notified", "expired", "done" ] }, "repeat": { "type": "boolean", "description": "If `true`, a new task is created when this one is done." }, "unplanned": { "type": "boolean", "description": "If `true`, the work is unplanned. For information only." }, "cost": { "type": "number", "description": "Cost in the currency of the user. For information only." }, "completion_date": { "type": [ "string", "null" ], "description": "Date the service work was completed." }, "start": { "type": "object", "description": "Counter values when the task was created.", "properties": { "mileage": { "type": "integer", "description": "Mileage in kilometres." }, "date": { "type": "string", "description": "Date and time." }, "engine_hours": { "type": "integer", "description": "Engine hours." } } }, "completion": { "allOf": [ { "type": "object", "description": "Date and counter values when the task was marked done.", "properties": { "mileage": { "type": "integer", "description": "Mileage in kilometres." }, "date": { "type": "string", "description": "Date and time." }, "engine_hours": { "type": "integer", "description": "Engine hours." } } } ], "description": "**Not editable.**" }, "current_position": { "type": "object", "description": "Current counter values.", "properties": { "mileage": { "type": "integer", "description": "Mileage in kilometres." }, "date": { "type": "string", "description": "Date and time." }, "engine_hours": { "type": "integer", "description": "Engine hours." } } }, "conditions": { "$ref": "#/components/schemas/ServiceTaskConditions" }, "notifications": { "$ref": "#/components/schemas/ServiceTaskNotifications" }, "file_ids": { "type": "array", "description": "IDs of attached files. One file may be attached to several service works, so deleting one task does not delete a shared file.", "items": { "type": "integer" } }, "prediction": { "type": [ "object", "null" ], "description": "**Legacy, no longer used.** Controlled by the `return_prediction` parameter on the list and download operations.", "properties": { "end_date": { "type": "string", "description": "Predicted completion date." }, "wear_percentage": { "type": "integer", "description": "Predicted wear percentage." } } } } }, "FileUploadTarget": { "type": "object", "description": "Where to upload a reserved file, and what to send with it. Shared by the service work and task form file operations.\n\n`fields` varies with the storage backend: internal storage returns a single `token`, while Amazon S3 returns the policy, key, signature and encryption headers that the upload must carry.", "properties": { "file_id": { "type": "integer", "description": "ID of the reserved file. Submit it as the value of the record it belongs to: a service work, or a form field." }, "url": { "type": "string", "description": "URL to POST the multipart upload to." }, "expires": { "type": "string", "description": "When this upload target stops being valid." }, "file_field_name": { "type": "string", "description": "Name to use for the file field in the upload request." }, "fields": { "type": "object", "description": "Additional fields to send with the upload. Send these before the file part, which must be last.", "properties": { "token": { "type": "string", "description": "Used to authenticate the upload." } }, "additionalProperties": true } } }, "Bill": { "type": "object", "description": "A bill raised against the user's account.", "properties": { "order_id": { "type": "integer", "description": "Unique bill ID." }, "created": { "type": "string", "description": "When the bill was created." }, "sum": { "type": "number", "description": "Bill sum, in the default currency of the panel." }, "status": { "type": "string", "description": "Bill order status. `created` means raised but not settled.", "enum": [ "created", "settled", "gathered", "shipped", "shipped_partially", "canceled" ] }, "positions": { "type": "array", "description": "Position names. A bill raised through `bill/create` carries exactly one.", "items": { "type": "string" } }, "link": { "type": "string", "description": "URL to the order." } } }, "PaymentSystem": { "type": "object", "description": "A payment system available to the user, with the settings its dealer configured.\n\nThe object is open: a payment system may return further properties specific to its type. `prices`, a map of product code to price, appears for `ios_inapp`. Stripe adds the `auto_payment_min`, `auto_payment_threshold_min` and `auto_payment_monthly_min` limits. Yandex adds `merchant_receipt_required` and, when that is set, `merchant_receipt`.", "properties": { "type": { "type": "string", "description": "Payment system type." }, "url": { "type": "string", "description": "URL to send payment information to." }, "account": { "type": "string", "description": "Optional. The dealer's account in the payment system, which is the eshopId for RBK and the merchant code for 2Checkout." }, "currency": { "type": "string", "description": "Three-letter ISO 4217 currency code." }, "payment_code": { "type": "string", "description": "Optional. Code for payments." }, "subscription_code": { "type": "string", "description": "Subscription code. For 2Checkout, formerly Avangate, this is the same as `payment_code` but for subscriptions." }, "methods": { "type": "array", "description": "Optional. Available payment methods. May be empty.", "items": { "type": "string" } }, "min_payment": { "type": "number", "description": "Optional. Smallest amount this payment system accepts." }, "max_payment": { "type": "number", "description": "Optional. Largest amount this payment system accepts." }, "auto_payments_enabled": { "type": "boolean", "description": "Whether automatic payments are switched on for this payment system." } }, "additionalProperties": true }, "Plan": { "type": "object", "description": "A device plan, called a tariff in older parts of the API.", "properties": { "id": { "type": "integer", "description": "Unique ID." }, "name": { "type": "string", "description": "The plan's label." }, "group_id": { "type": "integer", "description": "Group of plans. A user can only switch to a plan in the same group." }, "active": { "type": "boolean", "description": "`true` when users may switch to this plan on their own. A user can only switch to an active plan." }, "type": { "type": "string", "description": "Plan type. The published prose omits `every_year`.", "enum": [ "monthly", "everyday", "activeday", "every_year" ] }, "price": { "type": "number", "description": "Price per month for a `monthly` or `everyday` plan, or price per active day for an `activeday` plan." }, "early_change_price": { "type": "number", "description": "Price of changing from this plan to another when the last change was less than the freeze period ago, 30 days by default under the `tariff.freeze.period` config option. **Absent when the user may not change plan early**, in which case frequent changes are refused outright." }, "device_limit": { "type": "integer", "description": "Maximum number of devices per account on this plan." }, "has_reports": { "type": "boolean", "description": "`true` when reports are allowed." }, "paas_free": { "type": "boolean", "description": "`true` when this plan is free for the PaaS owner." }, "store_period": { "type": "string", "description": "Data storage period, such as `2h` for two hours, `3d` for three days, `5m` for five months, or `1y` for one year." }, "auto_prolong": { "type": "boolean", "description": "Whether the plan renews automatically. Not documented in the published object description." }, "features": { "type": "array", "description": "Features this plan makes available to the user.", "items": { "type": "string", "enum": [ "api", "app_tasks", "app_fleet", "batch_operations", "custom_maps", "event_notification", "geocoding", "lbs", "map_layers", "multilevel_access", "priority_support", "retranslation", "report_xls", "report_scheduled", "routing", "ui_mobile", "weblocator", "chat", "statuses", "street_view", "driver_journal", "checkin", "custom_fields", "task_route_import" ] } }, "map_filter": { "type": "object", "description": "Which maps the user may use.", "properties": { "exclusion": { "type": "boolean", "description": "When `true` the maps in `values` are the ones **not** available. When `false` they are the only ones available." }, "values": { "type": "array", "description": "Map types the filter applies to.", "items": { "type": "string" } } } } } }, "Transaction": { "type": "object", "description": "One billing transaction on the user's account.", "properties": { "description": { "type": "string", "description": "Transaction description." }, "type": { "type": "string", "description": "Type of transaction." }, "subtype": { "type": "string", "description": "Subtype of transaction." }, "timestamp": { "type": "string", "description": "When the transaction was created." }, "user_id": { "type": "integer", "description": "ID of the user who made the transaction." }, "dealer_id": { "type": "integer", "description": "ID of the dealer." }, "tracker_id": { "type": "integer", "description": "Tracker ID, or 0 when the transaction is not associated with a tracker." }, "amount": { "type": "number", "description": "Amount of money in the transaction. Can be negative: -10.0000 means 10 units were taken off the balance." }, "new_balance": { "type": "number", "description": "The user's money balance after the transaction." }, "old_balance": { "type": "number", "description": "The user's money balance before the transaction." }, "bonus_amount": { "type": "number", "description": "Amount of bonus used in the transaction. Can be negative." }, "new_bonus": { "type": "number", "description": "The user's bonus balance after the transaction." }, "old_bonus": { "type": "number", "description": "The user's bonus balance before the transaction." } } }, "JournalLocation": { "type": "object", "description": "A point with an address. Unlike a depot location it carries no radius.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address at this point." } } }, "DriverJournalEntry": { "type": "object", "description": "A categorised trip in the driver journal.\n\nOn create, everything except `id`, `employee_id`, `comment` and the two odometer values is required, and `id` must be absent. On update only `id`, `type` and `comment` may be sent, and every other field is rejected.", "properties": { "id": { "type": "integer", "description": "ID of the entry. Absent when creating." }, "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as the object ID. Must belong to the current account and not be blocked." }, "start_date": { "type": "string", "description": "Start date and time of the entry." }, "end_date": { "type": "string", "description": "End date and time of the entry. Must be after `start_date`." }, "employee_id": { "type": [ "integer", "null" ], "description": "ID of the employee driving. Must exist when given." }, "type": { "type": "string", "description": "Type of the entry.", "enum": [ "work", "personal", "other" ] }, "comment": { "type": [ "string", "null" ], "description": "Comment for the entry." }, "start_location": { "allOf": [ { "$ref": "#/components/schemas/JournalLocation" } ], "description": "Where the entry starts." }, "end_location": { "allOf": [ { "$ref": "#/components/schemas/JournalLocation" } ], "description": "Where the entry ends." }, "length": { "type": "number", "description": "Length of the trip, in kilometres." }, "start_odometer": { "type": [ "number", "null" ], "description": "Odometer value at the start." }, "end_odometer": { "type": [ "number", "null" ], "description": "Odometer value at the end." } } }, "Garage": { "type": "object", "description": "A depot: where vehicles are kept and maintained, with the people responsible for them.", "properties": { "id": { "type": "integer", "description": "Depot ID. Absent when creating, required when updating." }, "location": { "type": "object", "description": "Optional. Where the depot is. Omitted from a response when the stored location is not valid.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address of the depot." }, "radius": { "type": "integer", "description": "Radius in metres, from 1 to 300000.", "minimum": 1, "maximum": 300000 } } }, "mechanic_name": { "type": "string", "description": "Name of the mechanic. **Required**, up to 255 characters.", "maxLength": 255 }, "dispatcher_name": { "type": "string", "description": "Name of the dispatcher. **Required**, up to 255 characters.", "maxLength": 255 }, "organization_name": { "type": "string", "description": "Name of the organization. **Required**, up to 255 characters.", "maxLength": 255 } }, "required": [ "mechanic_name", "dispatcher_name", "organization_name" ] }, "Task": { "type": "object", "description": "A task: a place to visit, a window to visit it in, and the conditions that decide whether it was done.", "properties": { "id": { "type": "integer", "description": "Primary key. Used when updating; **ignored when creating**." }, "user_id": { "type": "integer", "description": "ID of the owning user, the office. **Read-only.**", "readOnly": true }, "tracker_id": { "type": [ "integer", "null" ], "description": "ID of the tracker this task is assigned to. **Ignored on update**, where reassignment goes through a separate operation.", "minimum": 1 }, "location": { "type": "object", "description": "The circular geofence for this task. Entering and leaving it are what the platform watches. Cannot be null.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address of the location. Set this to associate the task with an address." }, "radius": { "type": "integer", "description": "Radius of the zone in metres, from 1 to 300000.", "minimum": 1, "maximum": 300000 } } }, "label": { "type": "string", "description": "Name of the task. 1 to 200 characters, and not blank.", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "description": "Description of the task. Up to 1024 characters. **Required**, but may be an empty string.", "maxLength": 1024 }, "creation_date": { "type": "string", "description": "When the task was created. **Read-only.**", "readOnly": true }, "from": { "type": "string", "description": "Date after which the zone must be visited.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "Date before which the zone must be visited. Required, and cannot be in the past.", "examples": [ "2026-08-24 09:00:00" ] }, "external_id": { "type": [ "string", "null" ], "description": "Free text tying this record to an external system, such as an order number. 1 to 100 characters. For reference only.", "minLength": 1, "maxLength": 100 }, "status": { "type": "string", "description": "Current status. **Ignored on create and update.** `arrived` means the performer reached the geofence without completing the task, and `faulty` means it completed with problems.", "enum": [ "unassigned", "assigned", "done", "failed", "delayed", "arrived", "faulty" ] }, "status_change_date": { "type": "string", "description": "When the status last changed. **Ignored on create and update.**" }, "max_delay": { "type": "integer", "description": "Maximum allowed completion delay, in minutes. Default 0, up to 512000.", "minimum": 0, "maximum": 512000 }, "min_stay_duration": { "type": "integer", "description": "Minimum time that must be spent inside the zone for completion, in minutes. Default 0, up to 512000.", "minimum": 0, "maximum": 512000 }, "min_arrival_duration": { "type": "integer", "description": "Visits shorter than this do not count as an arrival, in minutes. Default 0, up to 512000. Not listed in the published object description.", "minimum": 0, "maximum": 512000 }, "arrival_date": { "type": [ "string", "null" ], "description": "When the tracker reached the zone, or null if it has not. **Read-only.**", "readOnly": true }, "stay_duration": { "type": "integer", "description": "Seconds spent inside the zone. **Read-only.**", "readOnly": true }, "origin": { "type": "string", "description": "How this record was created. **Ignored on create and update.** `imported` means it came from a spreadsheet.", "enum": [ "manual", "scheduled", "imported" ] }, "tags": { "type": "array", "description": "IDs of tags applied to this record.", "items": { "type": "integer" } }, "type": { "type": "string", "description": "Which kind of record this is.", "enum": [ "task", "checkpoint", "route" ] }, "form": { "allOf": [ { "$ref": "#/components/schemas/Form" } ], "description": "The attached form, when there is one." }, "form_template_id": { "type": "integer", "description": "ID of a form template. Read on create and update **only when `create_form` is `true`**." }, "fields": { "type": "object", "description": "Custom field values. Each key is a custom field ID **as a string**, and each value carries the field `type` and its `value`.", "additionalProperties": true }, "files": { "type": "array", "description": "Files associated with this record. **Read-only**, and not listed in the published object description.", "items": { "type": "object", "additionalProperties": true }, "readOnly": true } } }, "CheckedTask": { "type": "object", "description": "A task parsed from a batch, with the same fields as a task plus any per-row problems found while checking it.", "properties": { "id": { "type": "integer", "description": "Primary key. Used when updating; **ignored when creating**." }, "user_id": { "type": "integer", "description": "ID of the owning user, the office. **Read-only.**", "readOnly": true }, "tracker_id": { "type": [ "integer", "null" ], "description": "ID of the tracker this task is assigned to. **Ignored on update**, where reassignment goes through a separate operation.", "minimum": 1 }, "location": { "type": "object", "description": "The circular geofence for this task. Entering and leaving it are what the platform watches. Cannot be null.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address of the location. Set this to associate the task with an address." }, "radius": { "type": "integer", "description": "Radius of the zone in metres, from 1 to 300000.", "minimum": 1, "maximum": 300000 } } }, "label": { "type": "string", "description": "Name of the task. 1 to 200 characters, and not blank.", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "description": "Description of the task. Up to 1024 characters. **Required**, but may be an empty string.", "maxLength": 1024 }, "creation_date": { "type": "string", "description": "When the task was created. **Read-only.**", "readOnly": true }, "from": { "type": "string", "description": "Date after which the zone must be visited.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "Date before which the zone must be visited. Required, and cannot be in the past.", "examples": [ "2026-08-24 09:00:00" ] }, "external_id": { "type": [ "string", "null" ], "description": "Free text tying this record to an external system, such as an order number. 1 to 100 characters. For reference only.", "minLength": 1, "maxLength": 100 }, "status": { "type": "string", "description": "Current status. **Ignored on create and update.** `arrived` means the performer reached the geofence without completing the task, and `faulty` means it completed with problems.", "enum": [ "unassigned", "assigned", "done", "failed", "delayed", "arrived", "faulty" ] }, "status_change_date": { "type": "string", "description": "When the status last changed. **Ignored on create and update.**" }, "max_delay": { "type": "integer", "description": "Maximum allowed completion delay, in minutes. Default 0, up to 512000.", "minimum": 0, "maximum": 512000 }, "min_stay_duration": { "type": "integer", "description": "Minimum time that must be spent inside the zone for completion, in minutes. Default 0, up to 512000.", "minimum": 0, "maximum": 512000 }, "min_arrival_duration": { "type": "integer", "description": "Visits shorter than this do not count as an arrival, in minutes. Default 0, up to 512000. Not listed in the published object description.", "minimum": 0, "maximum": 512000 }, "arrival_date": { "type": [ "string", "null" ], "description": "When the tracker reached the zone, or null if it has not. **Read-only.**", "readOnly": true }, "stay_duration": { "type": "integer", "description": "Seconds spent inside the zone. **Read-only.**", "readOnly": true }, "origin": { "type": "string", "description": "How this record was created. **Ignored on create and update.** `imported` means it came from a spreadsheet.", "enum": [ "manual", "scheduled", "imported" ] }, "tags": { "type": "array", "description": "IDs of tags applied to this record.", "items": { "type": "integer" } }, "type": { "type": "string", "description": "Which kind of record this is.", "enum": [ "task", "checkpoint", "route" ] }, "form": { "allOf": [ { "$ref": "#/components/schemas/Form" } ], "description": "The attached form, when there is one." }, "form_template_id": { "type": "integer", "description": "ID of a form template. Read on create and update **only when `create_form` is `true`**." }, "fields": { "type": "object", "description": "Custom field values. Each key is a custom field ID **as a string**, and each value carries the field `type` and its `value`.", "additionalProperties": true }, "files": { "type": "array", "description": "Files associated with this record. **Read-only**, and not listed in the published object description.", "items": { "type": "object", "additionalProperties": true }, "readOnly": true }, "errors": { "type": "array", "description": "Problems found in this row. Absent when the row is clean.", "items": { "type": "object", "additionalProperties": true } } }, "additionalProperties": true }, "TaskCheckpoint": { "type": "object", "description": "A checkpoint: one stop on a route. It carries every field a task does, plus its position in the route.\n\nA checkpoint inherits its performer from its route, so `tracker_id` is ignored on update.", "properties": { "id": { "type": "integer", "description": "Primary key. Used when updating; **ignored when creating**." }, "user_id": { "type": "integer", "description": "ID of the owning user, the office. **Read-only.**", "readOnly": true }, "tracker_id": { "type": [ "integer", "null" ], "description": "ID of the tracker this checkpoint is assigned to. **Ignored on update**, where reassignment goes through a separate operation.", "minimum": 1 }, "location": { "type": "object", "description": "The circular geofence for this checkpoint. Entering and leaving it are what the platform watches. Cannot be null.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address of the location. Set this to associate the task with an address." }, "radius": { "type": "integer", "description": "Radius of the zone in metres, from 1 to 300000.", "minimum": 1, "maximum": 300000 } } }, "label": { "type": "string", "description": "Name of the checkpoint. 1 to 200 characters, and not blank.", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "description": "Description of the checkpoint. Up to 1024 characters. **Required**, but may be an empty string.", "maxLength": 1024 }, "creation_date": { "type": "string", "description": "When the checkpoint was created. **Read-only.**", "readOnly": true }, "from": { "type": "string", "description": "Date after which the zone must be visited.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "Date before which the zone must be visited. Required, and cannot be in the past.", "examples": [ "2026-08-24 09:00:00" ] }, "external_id": { "type": [ "string", "null" ], "description": "Free text tying this record to an external system, such as an order number. 1 to 100 characters. For reference only.", "minLength": 1, "maxLength": 100 }, "status": { "type": "string", "description": "Current status. **Ignored on create and update.** `arrived` means the performer reached the geofence without completing the checkpoint, and `faulty` means it completed with problems.", "enum": [ "unassigned", "assigned", "done", "failed", "delayed", "arrived", "faulty" ] }, "status_change_date": { "type": "string", "description": "When the status last changed. **Ignored on create and update.**" }, "max_delay": { "type": "integer", "description": "Maximum allowed completion delay, in minutes. Default 0, up to 512000.", "minimum": 0, "maximum": 512000 }, "min_stay_duration": { "type": "integer", "description": "Minimum time that must be spent inside the zone for completion, in minutes. Default 0, up to 512000.", "minimum": 0, "maximum": 512000 }, "min_arrival_duration": { "type": "integer", "description": "Visits shorter than this do not count as an arrival, in minutes. Default 0, up to 512000. Not listed in the published object description.", "minimum": 0, "maximum": 512000 }, "arrival_date": { "type": [ "string", "null" ], "description": "When the tracker reached the zone, or null if it has not. **Read-only.**", "readOnly": true }, "stay_duration": { "type": "integer", "description": "Seconds spent inside the zone. **Read-only.**", "readOnly": true }, "origin": { "type": "string", "description": "How this record was created. **Ignored on create and update.** `imported` means it came from a spreadsheet.", "enum": [ "manual", "scheduled", "imported" ] }, "tags": { "type": "array", "description": "IDs of tags applied to this record.", "items": { "type": "integer" } }, "type": { "type": "string", "description": "Which kind of record this is.", "enum": [ "task", "checkpoint", "route" ] }, "form": { "allOf": [ { "$ref": "#/components/schemas/Form" } ], "description": "The attached form, when there is one." }, "form_template_id": { "type": "integer", "description": "ID of a form template. Read on create and update **only when `create_form` is `true`**." }, "fields": { "type": "object", "description": "Custom field values. Each key is a custom field ID **as a string**, and each value carries the field `type` and its `value`.", "additionalProperties": true }, "files": { "type": "array", "description": "Files associated with this record. **Read-only**, and not listed in the published object description.", "items": { "type": "object", "additionalProperties": true }, "readOnly": true }, "parent_id": { "type": "integer", "description": "ID of the route this checkpoint belongs to.", "minimum": 1 }, "order": { "type": "integer", "description": "Zero-based position of this checkpoint within its route.", "minimum": 0 } } }, "TaskHistoryEntry": { "type": "object", "description": "One recorded change to a task.", "properties": { "id": { "type": "integer", "description": "Entry ID." }, "user_id": { "type": "integer", "description": "ID of the user the task belongs to." }, "task_id": { "type": "integer", "description": "ID of the task this entry belongs to." }, "event_date": { "type": "string", "description": "When the change happened." }, "operation": { "type": "string", "description": "What happened.", "enum": [ "create", "update", "assign", "status_change" ] }, "payload": { "type": "object", "description": "Depends on the operation. Typically the fields that changed: a full task for `create`, a diff for `update`, and the new status for `status_change`.", "additionalProperties": true } } }, "ExternalIdCounts": { "type": "array", "description": "How many records share each `external_id` involved in this call, so a caller can spot a duplicate. Present only when the record carries an `external_id`.", "items": { "type": "object", "properties": { "external_id": { "type": "string", "description": "The external ID." }, "count": { "type": "integer", "description": "How many records now carry it." } } } }, "Period": { "type": "object", "description": "A date and time range.", "properties": { "from": { "type": "string", "description": "Start of the range.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the range.", "examples": [ "2026-08-24 09:00:00" ] } } }, "FormFile": { "type": "object", "description": "A file attached to a form field.", "properties": { "id": { "type": "integer", "description": "File ID." }, "storage_id": { "type": "integer", "description": "ID of the storage holding the file." }, "user_id": { "type": "integer", "description": "ID of the owning user." }, "type": { "type": "string", "description": "What kind of file this is.", "enum": [ "image", "file" ] }, "created": { "type": "string", "description": "When the file record was created." }, "uploaded": { "type": [ "string", "null" ], "description": "When the file was uploaded, or null if it has not been." }, "name": { "type": "string", "description": "File name." }, "size": { "type": "integer", "description": "Size in bytes. For a file that is not uploaded yet, the maximum size allowed for the upload." }, "mime_type": { "type": "string", "description": "MIME type of the file." }, "metadata": { "type": "object", "description": "Metadata for the file. For an image this carries `orientation`, the EXIF orientation.", "additionalProperties": true }, "state": { "type": "string", "description": "Where the file is in its lifecycle.", "enum": [ "created", "in_progress", "uploaded", "deleted" ] }, "download_url": { "type": [ "string", "null" ], "description": "URL the file is available at, or null if it is not uploaded yet." }, "bindings": { "type": "object", "description": "Every entity this file is linked to. For a form field this carries `form_field` with `form_id`, `field_id` and `submitted`.", "additionalProperties": true }, "previews": { "type": [ "array", "null" ], "description": "Preview images for the file. May be null or empty for any file in any state.", "items": { "type": "object", "additionalProperties": true } } } }, "FormListEntry": { "type": "object", "description": "One form in the list, with the records it relates to.", "properties": { "form": { "allOf": [ { "$ref": "#/components/schemas/Form" } ], "description": "The form itself. Never null." }, "task": { "type": "object", "description": "The task the form belongs to: `id`, `label`, `from`, `to`, `creation_date`, `arrival_date`, `address`, `status`, `completion_date`, and `fact_duration`, how long the performer actually spent on it as an ISO 8601 duration.", "additionalProperties": true }, "employee": { "type": [ "object", "null" ], "description": "The employee who submitted it, when there is one.", "additionalProperties": true }, "tracker": { "type": [ "object", "null" ], "description": "The tracker involved, when there is one.", "additionalProperties": true }, "vehicle": { "type": [ "object", "null" ], "description": "The vehicle involved, when there is one.", "additionalProperties": true }, "submit_places": { "type": [ "object", "null" ], "description": "Where the form was submitted, and which places and geofences that location falls in. May be null.", "properties": { "location": { "type": "object", "description": "Where the form was submitted.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address at that point." } } }, "places": { "type": "array", "description": "Places containing the submission location. May be empty.", "items": { "type": "object", "additionalProperties": true } }, "zones": { "type": "array", "description": "Geofences containing the submission location. May be empty.", "items": { "type": "object", "additionalProperties": true } } } } } }, "TaskRoute": { "type": "object", "description": "A route: a named, ordered set of checkpoints. A route is complete when every checkpoint is complete and they were visited in order; otherwise it completes with warnings or fails.", "properties": { "id": { "type": "integer", "description": "Primary key. Used when updating; **ignored when creating**." }, "user_id": { "type": "integer", "description": "ID of the owning user. **Read-only.**", "readOnly": true }, "tracker_id": { "type": [ "integer", "null" ], "description": "ID of the tracker this route is assigned to. **Ignored on update**; use `task/route/assign`.", "minimum": 1 }, "label": { "type": "string", "description": "Name of the route. 1 to 200 characters, and not blank.", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "description": "Description of the route. Up to 1024 characters. **Required**, but may be an empty string.", "maxLength": 1024 }, "creation_date": { "type": "string", "description": "When the route was created. **Read-only.**", "readOnly": true }, "from": { "type": "string", "description": "Date after which the first checkpoint zone must be visited. **Ignored on create and update**: it is taken from the first checkpoint.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "Date before which the last checkpoint zone must be visited. **Ignored on create and update**: it is taken from the last checkpoint.", "examples": [ "2026-08-24 09:00:00" ] }, "external_id": { "type": [ "string", "null" ], "description": "Free text tying this route to an external system. 1 to 100 characters.", "minLength": 1, "maxLength": 100 }, "status": { "type": "string", "description": "Current status. **Ignored on create and update.**", "enum": [ "unassigned", "assigned", "done", "failed", "delayed", "arrived", "faulty" ] }, "status_change_date": { "type": "string", "description": "When the status last changed. **Ignored on create and update.**" }, "origin": { "type": "string", "description": "How this route was created. **Ignored on create and update.**", "enum": [ "manual", "scheduled", "imported" ] }, "tags": { "type": "array", "description": "IDs of tags applied to the route.", "items": { "type": "integer" } }, "checkpoint_ids": { "type": "array", "description": "IDs of the route checkpoints, in order of execution. **Ignored on create.** On update, reordering this array changes the order of execution.", "items": { "type": "integer" } }, "type": { "type": "string", "description": "Always `route`.", "enum": [ "route" ] } } }, "ScheduleParameters": { "type": "object", "description": "When a schedule fires. `type` selects which of the two forms applies, and the matching array must not be empty.", "properties": { "type": { "type": "string", "description": "Which form this is. `weekdays` repeats on days of the week, `month_days` on days of the month.", "enum": [ "weekdays", "month_days" ] }, "weekdays": { "type": "array", "description": "Days of the week to create tasks on, where 1 is Monday and 7 is Sunday. Used when `type` is `weekdays`.", "items": { "type": "integer", "minimum": 1, "maximum": 7 }, "minItems": 1, "maxItems": 7 }, "month_days": { "type": "array", "description": "Days of the month to create tasks on, from 1 to 31. Used when `type` is `month_days`.", "items": { "type": "integer", "minimum": 1, "maximum": 31 }, "minItems": 1, "maxItems": 31 } }, "required": [ "type" ] }, "TaskScheduleEntry": { "type": "object", "description": "A recurring task. Very like a task, except that the specific `from` and `to` of a task are replaced by `from_time`, `duration` and `parameters`, which together say when each generated task runs.", "properties": { "id": { "type": "integer", "description": "Primary key. Used when updating; **ignored when creating**." }, "user_id": { "type": "integer", "description": "ID of the owning user. **Read-only.**", "readOnly": true }, "tracker_id": { "type": [ "integer", "null" ], "description": "ID of the tracker every task generated from this schedule is assigned to. Null leaves them unassigned.", "minimum": 1 }, "label": { "type": "string", "description": "Name. 1 to 200 characters.", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "description": "Description. Up to 1024 characters. **Required**, but may be an empty string.", "maxLength": 1024 }, "location": { "type": "object", "description": "The circular geofence for the generated task. Cannot be null.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address of the location." }, "radius": { "type": "integer", "description": "Radius of the zone in metres, from 1 to 300000.", "minimum": 1, "maximum": 300000 } } }, "from_time": { "type": "string", "description": "Time of day at which the generated task starts, as `HH:MM:SS`." }, "duration": { "type": "integer", "description": "Total duration in minutes between the generated task's `from` and `to`. From 1 to 46080, which is 32 days.", "minimum": 1, "maximum": 46080 }, "max_delay": { "type": "integer", "description": "Maximum allowed completion delay, in minutes. Default 0, up to 512000.", "minimum": 0, "maximum": 512000 }, "min_stay_duration": { "type": "integer", "description": "Minimum time that must be spent inside the zone for completion, in minutes. Default 0, up to 512000.", "minimum": 0, "maximum": 512000 }, "min_arrival_duration": { "type": "integer", "description": "Visits shorter than this do not count as an arrival, in minutes. Default 0, up to 512000.", "minimum": 0, "maximum": 512000 }, "parameters": { "$ref": "#/components/schemas/ScheduleParameters" }, "tags": { "type": "array", "description": "IDs of tags applied to this schedule.", "items": { "type": "integer" } }, "form_template_id": { "type": [ "integer", "null" ], "description": "ID of a form template to build a form from on each generated task. Nullable.", "minimum": 1 }, "fields": { "type": "object", "description": "Custom field values, keyed by custom field ID as a string. Not listed in the published object description.", "additionalProperties": true }, "type": { "type": "string", "description": "Which kind of schedule this is. **Read-only**, and not listed in the published object description.", "enum": [ "task", "route", "checkpoint" ], "readOnly": true }, "files": { "type": "array", "description": "Files associated with this schedule. **Read-only**, and not listed in the published object description.", "items": { "type": "object", "additionalProperties": true }, "readOnly": true } } }, "RouteScheduleEntry": { "type": "object", "description": "A recurring route. It carries no location or timing of its own: those live on its checkpoint schedules.", "properties": { "id": { "type": "integer", "description": "Primary key. Used when updating; **ignored when creating**." }, "user_id": { "type": "integer", "description": "ID of the owning user. **Read-only.**", "readOnly": true }, "tracker_id": { "type": [ "integer", "null" ], "description": "ID of the tracker every task generated from this route schedule is assigned to. Null leaves them unassigned.", "minimum": 1 }, "label": { "type": "string", "description": "Name. 1 to 200 characters.", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "description": "Description. Up to 1024 characters. **Required**, but may be an empty string.", "maxLength": 1024 }, "parameters": { "$ref": "#/components/schemas/ScheduleParameters" } } }, "CheckpointScheduleEntry": { "type": "object", "description": "One stop on a recurring route. It carries the location and timing that a route schedule does not, and takes its firing days from its parent route schedule rather than holding `parameters` of its own.", "properties": { "id": { "type": "integer", "description": "Primary key. Used when updating; **ignored when creating**." }, "user_id": { "type": "integer", "description": "ID of the owning user. **Read-only.**", "readOnly": true }, "tracker_id": { "type": [ "integer", "null" ], "description": "ID of the tracker every task generated from this checkpoint schedule is assigned to. Null leaves them unassigned.", "minimum": 1 }, "label": { "type": "string", "description": "Name. 1 to 200 characters.", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "description": "Description. Up to 1024 characters. **Required**, but may be an empty string.", "maxLength": 1024 }, "parent_id": { "type": "integer", "description": "ID of the route schedule this checkpoint schedule belongs to.", "minimum": 1 }, "order": { "type": "integer", "description": "Zero-based position within the route schedule.", "minimum": 0 }, "location": { "type": "object", "description": "The circular geofence for the generated task. Cannot be null.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address of the location." }, "radius": { "type": "integer", "description": "Radius of the zone in metres, from 1 to 300000.", "minimum": 1, "maximum": 300000 } } }, "from_time": { "type": "string", "description": "Time of day at which the generated task starts, as `HH:MM:SS`." }, "duration": { "type": "integer", "description": "Total duration in minutes between the generated task's `from` and `to`. From 1 to 46080, which is 32 days.", "minimum": 1, "maximum": 46080 }, "max_delay": { "type": "integer", "description": "Maximum allowed completion delay, in minutes. Default 0, up to 512000.", "minimum": 0, "maximum": 512000 }, "min_stay_duration": { "type": "integer", "description": "Minimum time that must be spent inside the zone for completion, in minutes. Default 0, up to 512000.", "minimum": 0, "maximum": 512000 }, "min_arrival_duration": { "type": "integer", "description": "Visits shorter than this do not count as an arrival, in minutes. Default 0, up to 512000.", "minimum": 0, "maximum": 512000 }, "tags": { "type": "array", "description": "IDs of tags applied to this checkpoint schedule.", "items": { "type": "integer" } }, "form_template_id": { "type": [ "integer", "null" ], "description": "ID of a form template to build a form from on each generated task. Nullable.", "minimum": 1 }, "fields": { "type": "object", "description": "Custom field values, keyed by custom field ID as a string.", "additionalProperties": true } } }, "Place": { "type": "object", "description": "A point of interest: a business-specific place such as a shop, delivery point or warehouse that employees visit.\n\nPOIs can carry custom fields. A POI with a custom field of type \"responsible employee\" appears in the Mobile Tracker App for the employee it names.", "properties": { "id": { "type": "integer", "description": "ID of the POI." }, "icon_id": { "type": [ "integer", "null" ], "description": "Optional. ID of the assigned icon, 1 to 255. **Cannot be set here**: use `place/avatar/assign`.", "minimum": 1, "maximum": 255 }, "avatar_file_name": { "type": [ "string", "null" ], "description": "Optional. Name of the uploaded avatar file, set by `place/avatar/upload`." }, "location": { "type": "object", "description": "Where the POI is. Required.", "properties": { "lat": { "type": "number", "description": "Latitude. Required." }, "lng": { "type": "number", "description": "Longitude. Required." }, "address": { "type": "string", "description": "Address of the POI. Required, up to 255 characters.", "maxLength": 255 }, "radius": { "type": "integer", "description": "Radius of the POI in metres. Required, 1 to 300000.", "minimum": 1, "maximum": 300000 } } }, "fields": { "type": "object", "description": "Optional. Custom field values, keyed by custom field ID **as a string**.", "additionalProperties": true }, "label": { "type": "string", "description": "Name of the POI." }, "description": { "type": [ "string", "null" ], "description": "Optional. Description of the POI." }, "tags": { "type": "array", "description": "Optional. IDs of tags applied to the POI. Non-empty when present.", "items": { "type": "integer" }, "minItems": 1 }, "external_id": { "type": [ "string", "null" ], "description": "Optional. Free text tying this POI to an external system. Up to 32 characters.", "maxLength": 32 }, "files": { "type": "array", "description": "Files associated with the POI. **Read-only**, and not listed in the published object description.", "items": { "type": "object", "additionalProperties": true }, "readOnly": true } } }, "CheckedPlace": { "type": "object", "description": "A POI parsed from a batch, with the same fields as a POI plus any per-row problems found while checking it.", "properties": { "id": { "type": "integer", "description": "ID of the POI." }, "icon_id": { "type": [ "integer", "null" ], "description": "Optional. ID of the assigned icon, 1 to 255. **Cannot be set here**: use `place/avatar/assign`.", "minimum": 1, "maximum": 255 }, "avatar_file_name": { "type": [ "string", "null" ], "description": "Optional. Name of the uploaded avatar file, set by `place/avatar/upload`." }, "location": { "type": "object", "description": "Where the POI is. Required.", "properties": { "lat": { "type": "number", "description": "Latitude. Required." }, "lng": { "type": "number", "description": "Longitude. Required." }, "address": { "type": "string", "description": "Address of the POI. Required, up to 255 characters.", "maxLength": 255 }, "radius": { "type": "integer", "description": "Radius of the POI in metres. Required, 1 to 300000.", "minimum": 1, "maximum": 300000 } } }, "fields": { "type": "object", "description": "Optional. Custom field values, keyed by custom field ID **as a string**.", "additionalProperties": true }, "label": { "type": "string", "description": "Name of the POI." }, "description": { "type": [ "string", "null" ], "description": "Optional. Description of the POI." }, "tags": { "type": "array", "description": "Optional. IDs of tags applied to the POI. Non-empty when present.", "items": { "type": "integer" }, "minItems": 1 }, "external_id": { "type": [ "string", "null" ], "description": "Optional. Free text tying this POI to an external system. Up to 32 characters.", "maxLength": 32 }, "files": { "type": "array", "description": "Files associated with the POI. **Read-only**, and not listed in the published object description.", "items": { "type": "object", "additionalProperties": true }, "readOnly": true }, "errors": { "type": "array", "description": "Optional. Problems found in this row.", "items": { "type": "object", "additionalProperties": true } }, "tag_names": { "type": "array", "description": "Optional. Tag names of the POI, as written in the batch.", "items": { "type": "string" } } }, "additionalProperties": true }, "ImportProcess": { "type": "object", "description": "A background import of records from a preloaded TSV file.\n\n**One shape, shared by every import family**: places, vehicles, employees, zones and the rest all return this object, differing only in `type`.", "properties": { "id": { "type": "integer", "description": "Import process ID." }, "user_id": { "type": "integer", "description": "ID of the master user the import belongs to." }, "subuser_id": { "type": "integer", "description": "ID of the sub-user who started the import. Not listed in the published object description." }, "created": { "type": "string", "description": "When the import process was created." }, "type": { "type": "string", "description": "What is being imported.", "enum": [ "place", "vehicle", "employee", "driver", "fsm_employee", "zone", "customer" ] }, "params": { "type": "object", "description": "The import parameters, as given when it was started.", "properties": { "headers": { "type": "array", "description": "What each column of the file holds, in order.", "items": { "type": "string" } }, "user_headers": { "type": "array", "description": "Optional. Display labels for the columns.", "items": { "type": "string" } } } }, "filename": { "type": "string", "description": "Name of the preloaded TSV file being imported." }, "status": { "type": "string", "description": "Where the import is. `created` is waiting to start, `done` finished on its own, `failed` could not complete, and `finished` was closed by the user.", "enum": [ "created", "in_progress", "done", "failed", "finished" ] }, "status_change_date": { "type": "string", "description": "When the status last changed." }, "progress": { "type": "object", "description": "How far the import has got.", "properties": { "imported": { "type": "integer", "description": "Records imported successfully." }, "failed": { "type": "integer", "description": "Rows that did not pass validation." }, "percent": { "type": [ "integer", "null" ], "description": "Approximate percentage of rows processed." }, "processed_lines": { "type": "integer", "description": "Lines processed so far." }, "warnings": { "type": "array", "description": "The first 25 warnings.", "items": { "$ref": "#/components/schemas/ImportLineMessage" } }, "errors": { "type": "array", "description": "The first 25 errors.", "items": { "$ref": "#/components/schemas/ImportLineMessage" } } } } } }, "ImportLineMessage": { "type": "object", "description": "One problem found in the imported file, tied to the line it was found on.", "properties": { "line": { "type": "integer", "description": "Line number in the file." }, "message": { "type": "string", "description": "What was wrong with it." } } }, "Form": { "type": "object", "description": "A filled-in or fillable form. A form is created from a template and used once: after submission it is kept with its values for reference and cannot be reused.", "properties": { "id": { "type": "integer", "description": "Form ID." }, "label": { "type": "string", "description": "Form label, 1 to 100 characters.", "minLength": 1, "maxLength": 100 }, "description": { "type": "string", "description": "Form description. May be empty." }, "fields": { "type": "array", "description": "The form fields. See the form fields and values reference for the shape of each type.", "items": { "type": "object", "additionalProperties": true } }, "created": { "type": "string", "description": "When this form was created, or attached to its task. **Read-only.**", "readOnly": true }, "submit_in_zone": { "type": "boolean", "description": "When `true`, the form can only be submitted inside the task zone." }, "task_id": { "type": [ "integer", "null" ], "description": "ID of the task this form is attached to." }, "checkin_id": { "type": [ "integer", "null" ], "description": "ID of the check-in this form belongs to, when it came from one." }, "tracker_id": { "type": [ "integer", "null" ], "description": "ID of the tracker involved." }, "template_id": { "type": [ "integer", "null" ], "description": "ID of the template this form was built from. **Null when that template has been deleted.**" }, "template_version": { "type": [ "integer", "null" ], "description": "Version of the template at the time this form was created." }, "values": { "type": [ "object", "null" ], "description": "The submitted values, keyed by field ID. **Null when the form has not been filled in.** See the form fields and values reference.", "additionalProperties": true }, "submitted": { "type": [ "string", "null" ], "description": "When the values were last submitted." }, "submit_location": { "type": [ "object", "null" ], "description": "Where the values were last submitted.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address at that point." } } } } }, "FormTemplate": { "type": "object", "description": "A reusable set of form fields. Each form is issued from a template, and changing a template afterwards does not alter forms already issued from it.", "properties": { "id": { "type": "integer", "description": "ID of the template." }, "label": { "type": "string", "description": "Template label, 1 to 100 characters.", "minLength": 1, "maxLength": 100 }, "description": { "type": [ "string", "null" ], "description": "Template description. May be empty or null." }, "version": { "type": "integer", "description": "Auto-incrementing version number, raised on every modification." }, "fields": { "type": "array", "description": "The template fields. See the form fields and values reference for the shape of each type.", "items": { "type": "object", "additionalProperties": true } }, "created": { "type": "string", "description": "When this template was created. **Read-only.**", "readOnly": true }, "updated": { "type": "string", "description": "When this template was last modified. **Read-only.**", "readOnly": true }, "submit_in_zone": { "type": "boolean", "description": "When `true`, forms from this template can only be submitted inside the task zone." }, "default": { "type": "boolean", "description": "When `true`, this template is chosen by default for every new task that takes a form." } } }, "Employee": { "type": "object", "description": "A person working at the organization: an employee or a driver. Employees can be linked to trackers, vehicles and places.", "properties": { "id": { "type": [ "integer", "null" ], "description": "Internal ID. May be null or absent when creating." }, "tracker_id": { "type": [ "integer", "null" ], "description": "ID of the tracker currently assigned to this employee. Null means none." }, "first_name": { "type": "string", "description": "First name. **Cannot be empty.** Up to 100 characters.", "minLength": 1, "maxLength": 100 }, "middle_name": { "type": "string", "description": "Middle name. May be empty but **not null**. Up to 100 characters.", "maxLength": 100 }, "last_name": { "type": "string", "description": "Last name. May be empty but **not null**. Up to 100 characters.", "maxLength": 100 }, "email": { "type": "string", "description": "Email address, which must be valid when given. May be empty but **not null**. Up to 100 characters.", "maxLength": 100 }, "phone": { "type": "string", "description": "Phone number **without the leading `+`**. May be empty but not null. Up to 32 characters.", "maxLength": 32 }, "driver_license_number": { "type": "string", "description": "Driver licence number. May be empty but not null. Up to 32 characters.", "maxLength": 32 }, "driver_license_cats": { "type": "string", "description": "Driver licence categories. Up to 32 characters.", "maxLength": 32 }, "driver_license_issue_date": { "type": [ "string", "null" ], "description": "Issue date of the driver licence, as `yyyy-MM-dd`." }, "driver_license_valid_till": { "type": [ "string", "null" ], "description": "Date the driver licence is valid until, as `yyyy-MM-dd`." }, "hardware_key": { "type": [ "string", "null" ], "description": "Hardware key, such as an iButton or RFID identifier. Up to 64 characters.", "maxLength": 64 }, "icon_id": { "type": [ "integer", "null" ], "description": "ID of the assigned icon. **Cannot be set here**: use `employee/avatar/assign`." }, "avatar_file_name": { "type": [ "string", "null" ], "description": "Name of the uploaded avatar file. **Cannot be set here**: use `employee/avatar/upload`." }, "department_id": { "type": [ "integer", "null" ], "description": "ID of the department this employee belongs to." }, "location": { "type": [ "object", "null" ], "description": "Optional. Location associated with this employee. Must be a valid location or null.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address at that point." }, "radius": { "type": "integer", "description": "Radius in metres around that point." } } }, "personnel_number": { "type": "string", "description": "Optional. Personnel number. Up to 15 characters.", "maxLength": 15 }, "ssn": { "type": "string", "description": "Optional. Social security number. Up to 32 characters.", "maxLength": 32 }, "tags": { "type": "array", "description": "IDs of tags applied to the employee.", "items": { "type": "integer" } } } }, "CheckedEmployee": { "type": "object", "description": "An employee parsed from a batch, with the same fields as an employee plus any per-row problems found while checking it.", "properties": { "id": { "type": [ "integer", "null" ], "description": "Internal ID. May be null or absent when creating." }, "tracker_id": { "type": [ "integer", "null" ], "description": "ID of the tracker currently assigned to this employee. Null means none." }, "first_name": { "type": "string", "description": "First name. **Cannot be empty.** Up to 100 characters.", "minLength": 1, "maxLength": 100 }, "middle_name": { "type": "string", "description": "Middle name. May be empty but **not null**. Up to 100 characters.", "maxLength": 100 }, "last_name": { "type": "string", "description": "Last name. May be empty but **not null**. Up to 100 characters.", "maxLength": 100 }, "email": { "type": "string", "description": "Email address, which must be valid when given. May be empty but **not null**. Up to 100 characters.", "maxLength": 100 }, "phone": { "type": "string", "description": "Phone number **without the leading `+`**. May be empty but not null. Up to 32 characters.", "maxLength": 32 }, "driver_license_number": { "type": "string", "description": "Driver licence number. May be empty but not null. Up to 32 characters.", "maxLength": 32 }, "driver_license_cats": { "type": "string", "description": "Driver licence categories. Up to 32 characters.", "maxLength": 32 }, "driver_license_issue_date": { "type": [ "string", "null" ], "description": "Issue date of the driver licence, as `yyyy-MM-dd`." }, "driver_license_valid_till": { "type": [ "string", "null" ], "description": "Date the driver licence is valid until, as `yyyy-MM-dd`." }, "hardware_key": { "type": [ "string", "null" ], "description": "Hardware key, such as an iButton or RFID identifier. Up to 64 characters.", "maxLength": 64 }, "icon_id": { "type": [ "integer", "null" ], "description": "ID of the assigned icon. **Cannot be set here**: use `employee/avatar/assign`." }, "avatar_file_name": { "type": [ "string", "null" ], "description": "Name of the uploaded avatar file. **Cannot be set here**: use `employee/avatar/upload`." }, "department_id": { "type": [ "integer", "null" ], "description": "ID of the department this employee belongs to." }, "location": { "type": [ "object", "null" ], "description": "Optional. Location associated with this employee. Must be a valid location or null.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address at that point." }, "radius": { "type": "integer", "description": "Radius in metres around that point." } } }, "personnel_number": { "type": "string", "description": "Optional. Personnel number. Up to 15 characters.", "maxLength": 15 }, "ssn": { "type": "string", "description": "Optional. Social security number. Up to 32 characters.", "maxLength": 32 }, "tags": { "type": "array", "description": "IDs of tags applied to the employee.", "items": { "type": "integer" } }, "errors": { "type": "array", "description": "Optional. Problems found in this row.", "items": { "type": "object", "additionalProperties": true } } }, "additionalProperties": true }, "Department": { "type": "object", "description": "A group of employees, with an optional location.", "properties": { "id": { "type": "integer", "description": "ID of the department." }, "label": { "type": "string", "description": "Name of the department." }, "location": { "type": [ "object", "null" ], "description": "Optional. Location associated with the department. Must be a valid location or null.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address of the department." }, "radius": { "type": "integer", "description": "Radius of the zone in metres." } } } } }, "Checkin": { "type": "object", "description": "A check-in: a point in time and space recorded by a field employee from the Mobile Tracker App, with an optional comment, photos and a filled form.", "properties": { "id": { "type": "integer", "description": "ID of the check-in." }, "marker_time": { "type": "string", "description": "When the check-in was created. Never null." }, "user_id": { "type": "integer", "description": "ID of the master user. Never null." }, "tracker_id": { "type": "integer", "description": "ID of the tracker that created this check-in. Never null." }, "employee_id": { "type": [ "integer", "null" ], "description": "Optional. ID of the employee assigned to that tracker." }, "location": { "type": "object", "description": "Where the check-in was made. Never null.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address at that point." }, "precision": { "type": "integer", "description": "Accuracy of the fix in metres. Note this is `precision`, not the `radius` used elsewhere." } } }, "comment": { "type": [ "string", "null" ], "description": "Optional. A comment provided by the app user." }, "files": { "type": "array", "description": "Files attached to the check-in. Never null, may be empty.", "items": { "$ref": "#/components/schemas/FormFile" } }, "form_id": { "type": [ "integer", "null" ], "description": "ID of the form sent with the check-in." }, "form_label": { "type": [ "string", "null" ], "description": "Label of the form sent with the check-in." } } }, "Subuser": { "type": "object", "description": "An additional user with access to the master account, scoped to a subset of its trackers and limited by a security group.\n\nAlmost identical to a normal user. A sub-user owns nothing exclusively: every tracker, rule and task it creates belongs to the master account. The one exception is reports, where each sub-user has its own pool and schedule.", "properties": { "id": { "type": [ "integer", "null" ], "description": "Sub-user ID. May be null when creating." }, "activated": { "type": "boolean", "description": "`true` when the sub-user is allowed to log in." }, "login": { "type": "string", "description": "Sub-user email, used as the login. Must be a valid and unique email address." }, "first_name": { "type": "string", "description": "First name of the sub-user or contact person." }, "middle_name": { "type": "string", "description": "Middle name of the sub-user or contact person." }, "last_name": { "type": "string", "description": "Last name of the sub-user or contact person." }, "legal_type": { "type": "string", "description": "Legal type of the sub-user.", "enum": [ "legal_entity", "individual", "sole_trader" ] }, "phone": { "type": "string", "description": "Phone number, 10 to 15 digits." }, "post_country": { "type": "string", "description": "Country part of the postal address." }, "post_index": { "type": "string", "description": "Index part of the postal address." }, "post_region": { "type": "string", "description": "Region part of the postal address." }, "post_city": { "type": "string", "description": "City from the postal address." }, "post_street_address": { "type": "string", "description": "Street part of the postal address." }, "registered_country": { "type": "string", "description": "Country part of the registered address." }, "registered_index": { "type": "string", "description": "Index part of the registered address." }, "registered_region": { "type": "string", "description": "Region part of the registered address." }, "registered_city": { "type": "string", "description": "City from the registered address." }, "registered_street_address": { "type": "string", "description": "Street part of the registered address." }, "state_reg_num": { "type": "string", "description": "State registration number, such as an EIN in the USA or an OGRN in Russia. Up to 15 characters.", "maxLength": 15 }, "tin": { "type": "string", "description": "Taxpayer identification number, also called a VATIN or INN." }, "legal_name": { "type": "string", "description": "Legal name. For `legal_entity` only." }, "iec": { "type": "string", "description": "Optional. Industrial Enterprises Classifier, also called a KPP. Used in Russia, for `legal_entity` only." }, "security_group_id": { "type": [ "integer", "null" ], "description": "ID of the security group this sub-user belongs to. **Null means the default group, which has no rights at all.**" }, "creation_date": { "type": "string", "description": "When the sub-user was created. **Read-only**, and should not be sent on update.", "readOnly": true } } }, "SecurityGroup": { "type": "object", "description": "A named set of rights that can be given to sub-users.\n\nMost read operations need no right at all, so a sub-user in the default group can still read, subject to scoping. Most data-changing operations do need one.", "properties": { "id": { "type": [ "integer", "null" ], "description": "Group ID. May be null when creating." }, "label": { "type": "string", "description": "Group label." }, "privileges": { "type": "object", "description": "What the group allows.", "properties": { "rights": { "type": "array", "description": "Rights granted to the group. **`admin` cannot be granted**: it belongs to master users only.", "items": { "type": "string", "enum": [ "tracker_update", "tracker_configure", "tracker_set_output", "tracker_register", "tracker_rule_update", "tag_update", "task_update", "form_template_update", "zone_update", "place_update", "places_custom_fields_update", "employee_update", "vehicle_update", "video_monitoring" ] } }, "store_period": { "type": "string", "description": "Optional. How far back the group may view history, in the legacy duration format: `2h` for two hours, `3d` for three days, `5m` for five months, `1y` for one year." } } } } }, "Plugin": { "type": "object", "description": "A software module that changes how certain API calls behave, such as which tracker registration flows or report types are available.", "properties": { "id": { "type": "integer", "description": "ID of the plugin." }, "type": { "type": "string", "description": "Plugin type, such as `tracker_register` or `tracker_report`." }, "ui_module": { "type": "string", "description": "Name of the plugin's UI module." }, "module": { "type": "string", "description": "Name of the plugin module." }, "filter": { "type": "object", "description": "Which device models this plugin applies to.", "properties": { "exclusion": { "type": "boolean", "description": "When `true`, `values` lists the models **not** supported. When `false`, it lists the only ones supported." }, "values": { "type": "array", "description": "Regular expressions matching device model names.", "items": { "type": "string" } } } }, "parameters": { "type": "object", "description": "Plugin-specific parameters. **Omitted when null**, which it usually is.", "additionalProperties": true } } }, "ApiKey": { "type": "object", "description": "A credential equivalent to a session hash but with unlimited lifetime, intended for one integration each.\n\n**Treat it as a secret.** It can perform almost any action on the account, and it travels in cleartext, so calls must go over HTTPS.", "properties": { "hash": { "type": "string", "description": "The key itself, 32 characters. Send it as `Authorization: NVX `.", "minLength": 32, "maxLength": 32 }, "create_date": { "type": "string", "description": "When the key was created." }, "title": { "type": "string", "description": "Title given to the key, so it can be told apart from the others." } } }, "DealerUiSettings": { "type": "object", "description": "How a dealer monitoring interface should present itself. Returned to an unauthenticated caller, so it carries branding and defaults but no account data.", "properties": { "domain": { "type": "string", "description": "Domain of the interface." }, "service_title": { "type": "string", "description": "Name of the service, shown in the interface." }, "locale": { "type": "string", "description": "Default locale." }, "demo_login": { "type": "string", "description": "Login for the demo account." }, "demo_password": { "type": "string", "description": "Password for the demo account." }, "maps": { "type": "array", "description": "Map types available to this dealer users.", "items": { "type": "string" } }, "default_map": { "type": "object", "description": "Which map to show, and where, before the user has chosen.", "properties": { "type": { "type": "string", "description": "Default map type." }, "location": { "type": "object", "description": "Where the map is centred by default.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } }, "zoom": { "type": "integer", "description": "Default zoom level." } } }, "currency": { "type": "string", "description": "The dealer currency, as an ISO 4217 code." }, "payment_link": { "type": [ "string", "null" ], "description": "Link a user can follow to top up their account. Depends on the PaaS, and may be null or empty." }, "promo_url": { "type": "string", "description": "Customizable About company URL." }, "google_client_id": { "type": [ "string", "null" ], "description": "Client ID for working with the Google API, or null." }, "favicon": { "type": [ "string", "null" ], "description": "Path or URL to the interface favicon." }, "logo": { "type": [ "string", "null" ], "description": "Path or URL to the logo." }, "app_logo": { "type": [ "string", "null" ], "description": "Path or URL to the mobile app logo." }, "login_wallpaper": { "type": [ "string", "null" ], "description": "Path or URL to the login page wallpaper." }, "desktop_wallpaper": { "type": [ "string", "null" ], "description": "Path to the interface wallpaper, or null." }, "monitoring_logo": { "type": [ "string", "null" ], "description": "Path to the monitoring page logo, or null." }, "login_footer": { "type": "string", "description": "Footer included on the login page. HTML special characters are escaped as entities." }, "allow_registration": { "type": "boolean", "description": "True when self-registration is available to this dealer users." }, "show_mobile_apps": { "type": "boolean", "description": "True when mobile applications are offered to this dealer users." }, "show_call_notifications": { "type": "boolean", "description": "True when call notifications are available." }, "geocoder": { "type": "string", "description": "Default geocoder." }, "route_provider": { "type": "string", "description": "Default route provider." }, "measurement_system": { "type": "string", "description": "Measurement system to present values in." }, "display_model_features_link": { "type": "boolean", "description": "True to show a link to squaregps.com in the device model information." }, "color_theme": { "type": "string", "description": "Colour theme code, or an empty string for the default theme." }, "app_color_theme": { "type": "string", "description": "Mobile app colour theme code, or an empty string for the default theme." }, "privacy_policy_link": { "type": [ "string", "null" ], "description": "Privacy policy link. May be null or empty." }, "tos": { "type": [ "string", "null" ], "description": "Terms of service text. May be null or empty." }, "tracker_model_filter": { "type": "object", "description": "Which device models may be registered.", "properties": { "exclusion": { "type": "boolean", "description": "When true, the models in `values` are excluded rather than being the only ones allowed." }, "values": { "type": "array", "description": "Model names the filter applies to. An empty list means every model is available.", "items": { "type": "string" } } } }, "internal": { "type": "object", "description": "Additional options.", "properties": { "light_registration": { "type": "boolean", "description": "When true, use the simplified registration flow with a demo tracker." }, "demo_tracker_source_id": { "type": "integer", "description": "ID of the tracker created by light registration." }, "demo_tracker_label": { "type": "string", "description": "Label of the tracker created by light registration." }, "no_register_commands": { "type": "boolean", "description": "When true, do not send configuration commands to devices on activation." } } } }, "additionalProperties": true }, "HistoryEntry": { "type": "object", "description": "One recorded event: a rule firing, a device going offline, a task changing state, and so on.", "properties": { "id": { "type": "integer", "description": "ID of the event.", "format": "int64" }, "type": { "type": "string", "description": "Kind of device the event came from.", "enum": [ "socket", "tracker", "camera" ] }, "is_read": { "type": "boolean", "description": "`true` when the user has seen the notification and it has been marked read." }, "message": { "type": "string", "description": "Notification message, **shortened to 256 characters**. The untruncated text is in `extra.full_message` when it was shortened." }, "time": { "type": "string", "description": "When the notification was received." }, "event": { "type": "string", "description": "Event type. The available values come from `history/type/list`." }, "tracker_id": { "type": "integer", "description": "ID of the tracker, also known as `object_id`." }, "rule_id": { "type": [ "integer", "null" ], "description": "ID of the rule that produced the event, when there was one." }, "track_id": { "type": "integer", "description": "ID of the track the event happened on, or 0 when no track is associated." }, "address": { "type": [ "string", "null" ], "description": "Street address of `location`, repeated here for convenience. Null when the event carries no location." }, "location": { "type": "object", "description": "Where the event happened.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "precision": { "type": "integer", "description": "Accuracy of the fix in metres." }, "address": { "type": "string", "description": "Human-readable address of the point, or an empty string when none was resolved. **Nested here, not at the top level of the entry.**" } } }, "assets": { "type": "array", "description": "Assets linked to the event, each naming an identifier and a type. Present on entries the platform could associate with a vehicle or an employee.", "items": { "type": "object", "properties": { "id": { "type": "integer", "description": "Identifier of the asset." }, "type": { "type": "string", "description": "What kind of asset it is.", "enum": [ "vehicle", "employee" ] } } } }, "extra": { "type": "object", "description": "Event-specific extras. Which of these appear depends on the event type.", "properties": { "employee_full_name": { "type": "string", "description": "Full name of the employee the event relates to. Added by the history service when it can resolve one." }, "vehicle_label": { "type": "string", "description": "Label of the vehicle the event relates to. Added by the history service when it can resolve one." }, "task_id": { "type": [ "integer", "null" ], "description": "Related task ID." }, "parent_task_id": { "type": [ "integer", "null" ], "description": "Related parent task ID, for a task checkpoint event." }, "counter_id": { "type": [ "integer", "null" ], "description": "Related counter ID." }, "service_task_id": { "type": [ "integer", "null" ], "description": "Related service work ID." }, "checkin_id": { "type": [ "integer", "null" ], "description": "Related check-in ID." }, "place_ids": { "type": "array", "description": "Related place IDs.", "items": { "type": "integer" } }, "places_total_count": { "type": "integer", "description": "Total number of related places." }, "last_known_location": { "type": "boolean", "description": "`true` when the location may be out of date." }, "tracker_label": { "type": "string", "description": "Label of the related tracker." }, "emergency": { "type": "boolean", "description": "`true` for an emergency event, matching the same flag on the rule." }, "zone_ids": { "type": "array", "description": "Related geofence IDs.", "items": { "type": "integer" } }, "zone_labels": { "type": "array", "description": "Related geofence labels.", "items": { "type": "string" } }, "proximity_object_id": { "type": "integer", "description": "ID of the tracker involved in a proximity event." }, "employee_id": { "type": "integer", "description": "ID of the driver at the time of the event." }, "sensor_id": { "type": "integer", "description": "Related sensor ID." }, "sensor_name": { "type": "string", "description": "Related sensor name." }, "sensor_calculated_value": { "type": "string", "description": "Related sensor value." }, "full_message": { "type": "string", "description": "The full event message, present when `message` was shortened." }, "precision": { "type": "integer", "description": "Precision of the location in metres." }, "command": { "type": "object", "description": "Present on a `command_sent` event: a command dispatched from a tracker's Commands block.", "properties": { "name": { "type": "string", "description": "Name of the command that was sent." }, "param": { "type": [ "string", "null" ], "description": "Value substituted for the command's `<>` placeholder, when it has one." }, "response": { "type": "object", "description": "Delivery result. A `hardware` command populates only `success`; an `http` command populates `status` and `body` (and `error` in place of them when the request itself failed) instead, leaving `success` absent.", "properties": { "status": { "type": [ "integer", "null" ], "description": "HTTP status code returned by the destination endpoint. Present only for an `http` command." }, "body": { "type": [ "string", "null" ], "description": "Raw response body from the destination endpoint, truncated to fit the event's storage limit. Present only for an `http` command." }, "error": { "type": [ "string", "null" ], "description": "Error message, up to 256 characters, when the request to the destination endpoint could not be completed. Present only for an `http` command, and only when the request failed." }, "success": { "type": [ "boolean", "null" ], "description": "Whether the command was sent to the device. Present only for a `hardware` command." } } } } } }, "additionalProperties": true } } }, "Tag": { "type": "object", "description": "A label used to find things quickly. Tags can be applied to place, task, task_schedule, employee, vehicle, zone, tracker.", "properties": { "id": { "type": "integer", "description": "Tag ID." }, "avatar_file_name": { "type": [ "string", "null" ], "description": "Optional. File name of the uploaded avatar, with extension. **Cannot be set here**: use `tag/avatar/upload`." }, "name": { "type": "string", "description": "Name of the tag." }, "color": { "type": "string", "description": "Tag colour, as a three-byte RGB hex value such as `FF0000`." } } }, "CustomField": { "type": "object", "description": "A custom field added to an entity, so records of that entity can carry information the platform does not define.\n\nThe `type` fixes what the field accepts and **cannot be changed** once the field exists.", "properties": { "id": { "type": [ "integer", "null" ], "description": "Field identifier. **Null when creating**, which is how a new field is signalled." }, "label": { "type": "string", "description": "Name of the field. Must be unique among the fields of one entity." }, "type": { "type": "string", "description": "What the field holds. `text` takes up to 700 Unicode characters. `bigtext` takes up to 20000 but has reduced search and sorting. `email` and `phone` are validated as such. `decimal` runs from -999999999999.999999 to 999999999999.999999 and stores six decimal places. `integer` runs from -2^63 to 2^63-1. `employee` links to an employee and is the only type taking `params`.", "enum": [ "text", "bigtext", "email", "phone", "decimal", "integer", "employee" ] }, "required": { "type": "boolean", "description": "Whether the field must be filled in." }, "description": { "type": "string", "description": "Additional information about the field, up to 512 characters.", "maxLength": 512 }, "params": { "type": "object", "description": "Type-specific parameters. **Omit when the type needs none**, which is every type except `employee`.", "properties": { "responsible": { "type": "boolean", "description": "For an `employee` field. When `true`, records carrying this field can be shown to that employee in the Mobile Tracker App. **Only one employee field per entity may set this.**" } }, "additionalProperties": true } } }, "CustomizableEntity": { "type": "object", "description": "A class of records whose presentation and editable fields can be customized, such as places.\n\nWhich rights govern an entity depends on its type, so an account may see an entity in `entity/list` that it cannot read.", "properties": { "id": { "type": "integer", "description": "Entity identifier." }, "type": { "type": "string", "description": "Which class of records this is. A plain account carries `place` and `task`; the published prose names only `place`." }, "settings": { "type": "object", "description": "How records of this entity are presented.", "properties": { "layout": { "type": "object", "description": "How the fields are laid out. **Must account for every field of the entity**, built-in and custom, with none omitted and none repeated.", "properties": { "sections": { "type": "array", "description": "The sections the fields are grouped into. At least one section must exist.", "items": { "type": "object", "properties": { "label": { "type": "string", "description": "Name of the section." }, "field_order": { "type": "array", "description": "The fields in this section, in order. Built-in fields by name, custom fields by ID **as a string**. For `place` the built-in names are `label`, `location`, `tags` and `description`.", "items": { "type": "string" } } } }, "minItems": 1 } } } } } } }, "ReportTimeFilter": { "type": "object", "description": "Which part of each day, and which days of the week, the report should cover.", "properties": { "from": { "type": "string", "description": "Start time of day, such as `00:00:00`.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End time of day, such as `23:59:59`.", "examples": [ "2026-08-24 09:00:00" ] }, "weekdays": { "type": "array", "description": "Days of the week to include, where 1 is Monday and 7 is Sunday.", "items": { "type": "integer", "minimum": 1, "maximum": 7 } } } }, "ReportDescriptor": { "type": "object", "description": "A generated or in-progress report, with the parameters it was generated from.", "properties": { "id": { "type": "integer", "description": "Report ID, used to retrieve, download or delete it." }, "title": { "type": "string", "description": "Report title." }, "created": { "type": "string", "description": "When the report was created." }, "from": { "type": "string", "description": "Start of the reporting period, as given to `generate`.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the reporting period, as given to `generate`.", "examples": [ "2026-08-24 09:00:00" ] }, "time_filter": { "$ref": "#/components/schemas/ReportTimeFilter" }, "percent": { "type": "integer", "description": "How ready the report is, as a percentage." }, "type": { "type": "string", "description": "What kind of report this is." }, "parameters": { "type": "object", "description": "The parameters the report was generated from.", "properties": { "geocoder": { "type": "string", "description": "Geocoder used for addresses." }, "trackers": { "type": "array", "description": "Trackers the report covers.", "items": { "type": "integer" } }, "plugins": { "type": "array", "description": "Parameters of every plugin used to generate the report.", "items": { "type": "object", "additionalProperties": true } }, "include_places_in_address": { "type": "boolean", "description": "Whether place names are prepended to addresses in this report." }, "include_zones_in_address": { "type": "boolean", "description": "Whether geofence names are prepended to addresses in this report." }, "locale_info": { "type": "object", "description": "The locale, timezone and measurement system the report was rendered with.", "properties": { "locale": { "type": "string", "description": "Locale used." }, "time_zone": { "type": "string", "description": "Timezone used." }, "measurement_system": { "type": "string", "description": "Measurement system used." } } } }, "additionalProperties": true } } }, "ReportBody": { "type": "object", "description": "A generated report, rendered as data rather than as a file.\n\nThe outer structure is the same whatever plugin produced the report: a report holds sheets, a sheet holds sections, and a section is either a table or a block of text. **What varies by plugin is the column set**, so the cells inside a row are keyed by column name and are not fixed here.", "properties": { "id": { "type": "integer", "description": "Report ID." }, "title": { "type": "string", "description": "Report title." }, "created": { "type": "string", "description": "When the report was created." }, "from": { "type": "string", "description": "Start of the reporting period.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": "string", "description": "End of the reporting period.", "examples": [ "2026-08-24 09:00:00" ] }, "time_filter": { "$ref": "#/components/schemas/ReportTimeFilter" }, "sheets": { "type": "array", "description": "The sheets of the report, normally one per tracker or per entity covered.", "items": { "type": "object", "properties": { "header": { "type": "string", "description": "Sheet heading, such as the employee and vehicle it covers." }, "entity_ids": { "type": "array", "description": "IDs of the entities this sheet covers.", "items": { "type": "integer" } }, "additional_field": { "type": "string", "description": "Extra text carried with the sheet. Often empty." }, "sections": { "type": "array", "description": "The sections making up the sheet.", "items": { "$ref": "#/components/schemas/ReportSection" } } } } } }, "additionalProperties": true }, "ReportSection": { "type": "object", "description": "One block of a report sheet. `type` says which shape it takes: a `table` carries `columns` and `data`, while a `text` block carries `rows` and a `style`.", "properties": { "type": { "type": "string", "description": "What kind of section this is.", "enum": [ "table", "text" ] }, "header": { "type": "string", "description": "Section heading, such as \"Trips\"." }, "style": { "type": "string", "description": "Rendering style for a text section, such as `small_print`." }, "columns": { "type": "array", "description": "Column definitions, for a table section. The `field` of each column is the key its cells use in a row.", "items": { "type": "object", "properties": { "field": { "type": "string", "description": "Key this column uses in a row object." }, "title": { "type": "string", "description": "Column heading as rendered, which may contain a line break." }, "align": { "type": "string", "description": "How the column is aligned.", "enum": [ "left", "right", "center" ] }, "width": { "type": "integer", "description": "Relative width of the column." }, "weight": { "type": "integer", "description": "Relative weight used when laying the table out." }, "highlight_min_max": { "type": "boolean", "description": "Whether the smallest and largest values in this column are highlighted." } } } }, "column_groups": { "type": "array", "description": "Groupings applied over the columns. Often empty.", "items": { "type": "object", "additionalProperties": true } }, "data": { "type": "array", "description": "The table body, in groups. Each group carries its own `header` and a list of rows.", "items": { "type": "object", "properties": { "header": { "type": "string", "description": "Heading for this group of rows, such as a date." }, "rows": { "type": "array", "description": "The rows. **Each row is keyed by column `field` name**, so its properties depend on the plugin. Every cell is a `ReportCell`.", "items": { "type": "object", "additionalProperties": { "$ref": "#/components/schemas/ReportCell" } } } } } }, "rows": { "type": "array", "description": "Summary rows, for a text section. Unlike table rows these are a flat list, each naming what it measures.", "items": { "type": "object", "properties": { "name": { "type": "string", "description": "What this row measures, such as \"Total trips length, km\"." }, "v": { "type": "string", "description": "The value as rendered for display." }, "raw": { "type": "number", "description": "The same value unformatted, for calculation." }, "highlight": { "type": "boolean", "description": "Whether this row is highlighted." } } } } }, "additionalProperties": true }, "ReportCell": { "type": "object", "description": "One cell of a report table. **`v` is for display and `raw` is for calculation**: `v` is already formatted to the report locale and units, while `raw` carries the underlying number or timestamp.", "properties": { "v": { "type": "string", "description": "The value as rendered for display." }, "raw": { "type": "number", "description": "The same value unformatted: a number, or a timestamp in milliseconds." }, "type": { "type": "string", "description": "What kind of cell this is, such as `value`." }, "location": { "type": "object", "description": "Where the value happened, on cells that carry a position.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } } }, "additionalProperties": true }, "ReportScheduleEntry": { "type": "object", "description": "A report that is generated regularly and emailed.", "properties": { "id": { "type": "integer", "description": "Schedule ID. **Ignored on create.**" }, "enabled": { "type": "boolean", "description": "Whether the scheduled report is active." }, "parameters": { "type": "object", "description": "What to generate, how often, and where to send it.", "properties": { "period": { "type": "string", "description": "How much time each report covers: `Xm` for months, `w` for a week, `d` for a day, `y` for a year." }, "schedule": { "type": "object", "description": "When the report fires.", "properties": { "type": { "type": "string", "description": "Which form the schedule takes.", "enum": [ "weekdays", "month_days" ] }, "weekdays": { "type": "array", "description": "Days of the week to fire on, where 1 is Monday and 7 is Sunday.", "items": { "type": "integer", "minimum": 1, "maximum": 7 } }, "month_days": { "type": "array", "description": "Days of the month to fire on, from 1 to 31.", "items": { "type": "integer", "minimum": 1, "maximum": 31 } } } }, "report": { "type": "object", "description": "The report to generate. Takes the same fields as `report/tracker/generate`, except the period, which comes from `period` above. Includes `include_places_in_address` and `include_zones_in_address`, both defaulting to `true`.", "additionalProperties": true }, "emails": { "type": "array", "description": "Optional. Addresses to send the report to.", "items": { "type": "string" } }, "email_format": { "type": "string", "description": "Format to send the report in.", "enum": [ "pdf", "xls" ] }, "email_zip": { "type": "boolean", "description": "Whether to compress the attachment." }, "sending_time": { "type": "string", "description": "Optional. Default `00:00:00`. Local time to send at, to **hourly granularity**." } } }, "fire_time": { "type": "string", "description": "When the schedule last fired. **Ignored on create and update.**" }, "last_result": { "type": "object", "description": "How the last generation went. **Ignored on create and update.**", "properties": { "success": { "type": "boolean", "description": "Whether the last report generated successfully." }, "id": { "type": "integer", "description": "ID of the report that was generated." } } } } }, "Zone": { "type": "object", "description": "A geofence: an area used to limit where a rule applies, and named after the address in reports when an event happens inside it.\n\n**One object, three shapes, selected by `type`.** A `circle` has `center` and `radius`. A `polygon` has `points`. A `sausage` has `points` and a `radius`, and covers everything within that distance of the polyline.", "properties": { "id": { "type": "integer", "description": "Geofence ID. Absent when creating." }, "type": { "type": "string", "description": "Which shape this geofence is, and therefore which of the fields below apply. **Cannot be changed** once the geofence exists.", "enum": [ "circle", "polygon", "sausage" ] }, "label": { "type": "string", "description": "Geofence label." }, "address": { "type": "string", "description": "Geofence address." }, "color": { "type": "string", "description": "Geofence colour, as a three-byte RGB hex value. Optional on create and update, defaulting to `27A9E3`." }, "radius": { "type": "integer", "description": "Radius in metres. **For `circle` it is the circle radius; for `sausage` it is the distance from the polyline. A `polygon` has no radius.**" }, "center": { "type": "object", "description": "Centre of the circle. **`circle` only.**", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } }, "points": { "type": "array", "description": "The geofence points. **`polygon` and `sausage` only**, and returned only when the operation was asked for them. On create they go in a separate `points` parameter instead.", "items": { "$ref": "#/components/schemas/ZonePoint" } }, "bounds": { "type": "object", "description": "North-west and south-east corners of the axis-aligned minimum bounding box.", "properties": { "nw": { "type": "object", "description": "North-west corner.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } }, "se": { "type": "object", "description": "South-east corner.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } } } }, "tags": { "type": "array", "description": "IDs of tags applied to the geofence.", "items": { "type": "integer" } } } }, "CheckedZone": { "type": "object", "description": "A circle geofence parsed from a batch, with the same fields as a geofence plus any per-row problems found while checking it. `id` is null, since nothing was created.", "properties": { "id": { "type": "integer", "description": "Geofence ID. Absent when creating." }, "type": { "type": "string", "description": "Which shape this geofence is, and therefore which of the fields below apply. **Cannot be changed** once the geofence exists.", "enum": [ "circle", "polygon", "sausage" ] }, "label": { "type": "string", "description": "Geofence label." }, "address": { "type": "string", "description": "Geofence address." }, "color": { "type": "string", "description": "Geofence colour, as a three-byte RGB hex value. Optional on create and update, defaulting to `27A9E3`." }, "radius": { "type": "integer", "description": "Radius in metres. **For `circle` it is the circle radius; for `sausage` it is the distance from the polyline. A `polygon` has no radius.**" }, "center": { "type": "object", "description": "Centre of the circle. **`circle` only.**", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } }, "points": { "type": "array", "description": "The geofence points. **`polygon` and `sausage` only**, and returned only when the operation was asked for them. On create they go in a separate `points` parameter instead.", "items": { "$ref": "#/components/schemas/ZonePoint" } }, "bounds": { "type": "object", "description": "North-west and south-east corners of the axis-aligned minimum bounding box.", "properties": { "nw": { "type": "object", "description": "North-west corner.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } }, "se": { "type": "object", "description": "South-east corner.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } } } }, "tags": { "type": "array", "description": "IDs of tags applied to the geofence.", "items": { "type": "integer" } }, "errors": { "type": "array", "description": "Optional. Problems found in this row, present only when something is wrong.", "items": { "type": "object", "properties": { "parameter": { "type": "string", "description": "Which parameter was wrong, such as `zone.center`." }, "error": { "type": "string", "description": "What was wrong with it." } } } } }, "additionalProperties": true }, "ZonePoint": { "type": "object", "description": "One point of a polygon or sausage geofence. A circle geofence has no points.", "properties": { "lat": { "type": "number", "description": "Point latitude." }, "lng": { "type": "number", "description": "Point longitude." }, "node": { "type": "boolean", "description": "`true` when this point is a route node." } } }, "AssetId": { "type": "object", "description": "A reference to an asset, by ID and kind.", "properties": { "id": { "type": "integer", "description": "ID of the asset." }, "type": { "type": "string", "description": "What kind of asset it is.", "enum": [ "employee", "vehicle" ] } } }, "AssetGroup": { "type": "object", "description": "A group of assets, holding at most one asset of each type.", "properties": { "id": { "type": "integer", "description": "ID of the group." }, "name": { "type": "string", "description": "Name of the group." }, "assets": { "type": "array", "description": "Assets in the group. May be empty.", "items": { "$ref": "#/components/schemas/AssetId" } } } }, "AssetGroupObject": { "type": "object", "description": "One asset and the group it belongs to. Used to report assignments that were removed or that block a reassignment.", "properties": { "asset": { "$ref": "#/components/schemas/AssetId" }, "group_id": { "type": "integer", "description": "ID of the group." } } }, "Track": { "type": "object", "description": "One track: a period of movement reconstructed from the points a device sent. The same object serves tracker tracks and asset tracks. **`type` selects which fields are present**, and the four kinds are not interchangeable.\n\n`regular` is an ordinary trip. `single_report` appears when the device reports in interval mode or only one point exists, and has no end or length. `merged` is every track in the period combined, returned only when `split` was `false`. `cluster` is a group of single-point trips gathered by coordinate, returned only when `cluster_single_reports` was `true`.", "properties": { "id": { "type": "integer", "description": "Track ID. Present on `regular` and `single_report`, absent on `merged` and `cluster`." }, "type": { "type": "string", "description": "Which kind of track this is.", "enum": [ "regular", "single_report", "merged", "cluster" ] }, "start_date": { "type": "string", "description": "Track start, in the user's time zone. On `single_report` it is when the point was registered." }, "start_address": { "type": "string", "description": "Address at the start." }, "end_date": { "type": "string", "description": "Track end, in the user's time zone. Absent on `single_report`." }, "end_address": { "type": "string", "description": "Address at the end. Absent on `single_report` and `cluster`." }, "length": { "type": "number", "description": "Track length in kilometres. Absent on `single_report` and `cluster`." }, "points": { "description": "**One field name, two meanings, decided by `type`.** On `regular` and `merged` it is an integer, the total number of points in the track. On `cluster` it is a plain list of coordinates, the points gathered into the cluster, which is not the same thing as `points_list`. Absent on `single_report`.", "oneOf": [ { "type": "integer", "description": "Total number of points in the track. `regular` and `merged`." }, { "type": "array", "description": "The coordinates gathered into this cluster. `cluster` only.", "items": { "type": "object", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } } } ] }, "max_speed": { "type": "integer", "description": "Highest speed recorded, in km/h. Absent on `single_report` and `cluster`." }, "avg_speed": { "type": "integer", "description": "Average speed in km/h. Absent on `cluster`." }, "event_count": { "type": "integer", "description": "Events recorded during the track. **Absent unless `count_events` was `true`.**" }, "norm_fuel_consumed": { "type": "number", "description": "Fuel consumed during the track, in litres. **Absent when no vehicle is linked to the tracker, or the vehicle has no `norm_avg_fuel_consumption`.**" }, "precision": { "type": "integer", "description": "Accuracy of the location in metres. Present on `single_report` and `cluster`, and depends on the device model." }, "gsm_lbs": { "type": "boolean", "description": "Optional. GSM LBS flag. On a `cluster` it is `true` only when every point in it is GSM LBS." }, "points_list": { "type": "array", "description": "The track points. **Returned only when `with_points` was `true`.**", "items": { "$ref": "#/components/schemas/TrackPoint" } }, "bounds": { "type": "object", "description": "North-west and south-east corners of the axis-aligned minimum bounding box.", "properties": { "nw": { "type": "object", "description": "North-west corner.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } }, "se": { "type": "object", "description": "South-east corner.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } } } } } }, "TrackPoint": { "type": "object", "description": "One point recorded by a tracker, timestamped when the tracker recorded it and shown in the user's time zone.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "alt": { "type": "integer", "description": "Altitude in metres." }, "satellites": { "type": "integer", "description": "How many GPS satellites fixed this point." }, "mileage": { "type": "number", "description": "Mileage." }, "get_time": { "type": "string", "description": "GPS timestamp of the point, in the user's time zone." }, "address": { "type": "string", "description": "Address at the point, or an empty string when none was recorded. **An address is recorded only at the start or end of a trip, or where an event happened.**" }, "heading": { "type": "integer", "description": "Direction in degrees, 0 to 360, where 0 is North.", "minimum": 0, "maximum": 360 }, "speed": { "type": "integer", "description": "Speed in km/h." }, "precision": { "type": "integer", "description": "Optional. Accuracy in metres. Depends on the device model." }, "gsm_lbs": { "type": "boolean", "description": "Optional. `true` when the location came from GSM LBS rather than GPS." }, "parking": { "type": "boolean", "description": "Optional. `true` when the point is not part of a trip. Which points count as parking depends on the platform's parking detection settings." }, "buffered": { "type": "boolean", "description": "Optional. `true` when the point was held in the device's memory and sent later. Varies by tracker model." } } }, "ApnSettings": { "type": "object", "description": "Mobile-internet access point settings for a SIM, together with the operator they belong to.", "properties": { "name": { "type": "string", "description": "APN name, for example `internet`." }, "user": { "type": "string", "description": "APN user name. Empty when the operator needs none." }, "password": { "type": "string", "description": "APN password. Empty when the operator needs none." }, "operator_name": { "type": "string", "description": "Name of the mobile operator the number belongs to." } } }, "BeaconDataEntry": { "type": "object", "description": "One scan of a BLE beacon by a tracker: which tag was seen, how strongly, and where the tracker was at the time.", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker that saw the beacon, also known as the object ID." }, "hardware_id": { "type": "string", "description": "Hardware ID of the beacon." }, "rssi": { "type": "integer", "description": "Received signal strength indicator, in dBm. Values closer to zero mean the beacon was nearer the tracker." }, "get_time": { "type": "string", "description": "When the scan was recorded, in `YYYY-MM-DD HH:MM:SS` format." }, "latitude": { "type": "number", "description": "Latitude of the tracker at the time of the scan." }, "longitude": { "type": "number", "description": "Longitude of the tracker at the time of the scan." }, "ext_data": { "type": "object", "description": "Additional values the beacon reported. Contents depend on the beacon type: an iBeacon reports `major` and `minor`, a sensor beacon may report `voltage` and `temperature`.", "additionalProperties": true } } }, "GeoBounds": { "type": "object", "description": "A bounding box, given by its north-west and south-east corners.", "properties": { "nw": { "type": "object", "description": "North-west corner.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } }, "se": { "type": "object", "description": "South-east corner.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } } } }, "AddressDetails": { "type": "object", "description": "An address broken into its parts. Returned only when `with_details` is set, and each part is present only when the geocoder resolved it.", "properties": { "country": { "type": "string", "description": "Optional. Country." }, "province": { "type": "string", "description": "Optional. Province, region or state." }, "locality": { "type": "string", "description": "Optional. City, town or village." }, "street": { "type": "string", "description": "Optional. Street." }, "house": { "type": "string", "description": "Optional. House number." }, "postcode": { "type": "string", "description": "Optional. Postal code." }, "bounds": { "allOf": [ { "$ref": "#/components/schemas/GeoBounds" } ], "description": "Optional. The smallest box that fully contains the result." } } }, "GeocodedLocation": { "type": "object", "description": "One result of a forward geocoding search: a point and the address at it.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "The matched address." }, "details": { "allOf": [ { "$ref": "#/components/schemas/AddressDetails" } ], "description": "Present only when `with_details` was set." } } }, "MapLayer": { "type": "object", "description": "A user-supplied KML overlay shown on the map. This object is the metadata only; the KML body is read separately.", "properties": { "id": { "type": "integer", "description": "Map layer ID. Read-only, assigned on upload, and required when updating.", "minimum": 1 }, "label": { "type": "string", "description": "Name of the layer. Trimmed, and cannot be empty.", "minLength": 1 } } }, "Retranslator": { "type": "object", "description": "A forwarding rule that sends the data devices report to the platform on to a third-party server.", "properties": { "id": { "type": "integer", "description": "Retranslator ID. Read-only, assigned on creation, and required when updating.", "minimum": 1 }, "name": { "type": "string", "description": "Name of the retranslator. 1 to 64 characters, trimmed.", "minLength": 1, "maxLength": 64 }, "protocol_id": { "type": "integer", "description": "ID of the protocol to forward with, from `retranslator/protocols/list`.", "minimum": 1 }, "address": { "type": "string", "description": "Network address of the receiving server, such as `127.0.0.1` or `localhost`. 1 to 255 characters, trimmed.", "minLength": 1, "maxLength": 255 }, "port": { "type": "integer", "description": "Port on the receiving server.", "minimum": 1, "maximum": 65535 }, "login": { "type": "string", "description": "Optional unless the protocol requires it. 1 to 64 characters, trimmed.", "minLength": 1, "maxLength": 64 }, "password": { "type": "string", "description": "Optional unless the protocol requires it. 1 to 64 characters, trimmed. **Returned in clear text by `retranslator/list`.**", "minLength": 1, "maxLength": 64 }, "enabled": { "type": "boolean", "description": "Whether the retranslator is forwarding." } } }, "RetranslatorProtocol": { "type": "object", "description": "A forwarding protocol the platform can retranslate with, and the credential rules it imposes.", "properties": { "id": { "type": "integer", "description": "Protocol ID." }, "name": { "type": "string", "description": "Protocol name, for example `EGTS` or `Wialon IPS`." }, "classname": { "type": "string", "description": "Internal name of the handler implementing the protocol. Read-only, and of no use to callers." }, "has_login": { "type": "boolean", "description": "`true` when the protocol uses a login at all." }, "has_password": { "type": "boolean", "description": "`true` when the protocol uses a password at all." }, "required_login": { "type": "boolean", "description": "`true` when a retranslator on this protocol cannot be saved without a login." }, "required_password": { "type": "boolean", "description": "`true` when a retranslator on this protocol cannot be saved without a password." }, "fake_device_id_pattern": { "type": [ "string", "null" ], "description": "Regular expression the substitute device ID must match, or `null` when the protocol does not constrain it." } } }, "WorkingStatus": { "type": "object", "description": "One entry in a status list: an activity a tracker can be marked as being in, such as busy or on a break.", "properties": { "id": { "type": "integer", "description": "Working status ID. Read-only, assigned on creation, and required when updating.", "minimum": 1 }, "label": { "type": "string", "description": "Human-readable label. 1 to 200 printable characters, trimmed, and cannot be blank.", "minLength": 1, "maxLength": 200 }, "color": { "type": "string", "description": "Display colour as six hexadecimal digits, **without a leading `#`**, for example `E57373`.", "pattern": "^[0-9a-fA-F]{6}$" } } }, "StatusChange": { "type": "object", "description": "One change of a tracker's working status, recording what it changed from and to, where, and who changed it. May be `null` where a tracker's status has never changed.", "properties": { "id": { "type": "integer", "description": "ID of the change record." }, "old_status_id": { "type": [ "integer", "null" ], "description": "The status held before the change, or `null` when none was set." }, "new_status_id": { "type": [ "integer", "null" ], "description": "The status held after the change, or `null` when the status was removed, which happens when the tracker is moved to a different status list." }, "location": { "type": [ "object", "null" ], "description": "Where the tracker was when the status changed, or `null` when the location was unknown.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "address": { "type": "string", "description": "Address at that point, or an empty string when none was resolved." } } }, "changed": { "type": "string", "description": "When the change was recorded, in `YYYY-MM-DD HH:MM:SS` format." }, "origin": { "type": "string", "description": "Who made the change: `supervisor` from the monitoring interface, `employee` from the Mobile Tracker App.", "enum": [ "supervisor", "employee" ] } } }, "TrackerCurrentStatus": { "type": "object", "description": "A tracker's working status as it stands now, with the change that produced it.", "properties": { "current_status": { "oneOf": [ { "$ref": "#/components/schemas/WorkingStatus" }, { "type": "null" } ], "description": "The status the tracker holds now, or `null` when none is set." }, "last_change": { "$ref": "#/components/schemas/StatusChange" } } }, "LocationLinkLifetime": { "type": "object", "description": "When a geo link is valid. Both ends are optional: with neither, the link never starts pending and never expires.", "properties": { "from": { "type": [ "string", "null" ], "description": "Optional. When the link becomes usable, in `YYYY-MM-DD HH:MM:SS` format. Before this the link sorts as pending.", "examples": [ "2026-08-24 09:00:00" ] }, "to": { "type": [ "string", "null" ], "description": "Optional. When the link expires, in `YYYY-MM-DD HH:MM:SS` format.", "examples": [ "2026-08-24 09:00:00" ] } } }, "LocationLinkTracker": { "type": "object", "description": "One tracker on a geo link, and what the link reveals about it.", "properties": { "tracker_id": { "type": "integer", "description": "ID of the tracker." }, "alias": { "type": [ "string", "null" ], "description": "Optional. Name to show instead of the tracker label, so the recipient need not see internal naming. At most 100 characters.", "maxLength": 100 }, "params": { "type": "object", "description": "What the link shows for this tracker.", "properties": { "object_data": { "type": "array", "description": "Which pieces of information to reveal. Empty means location only.", "items": { "type": "string", "description": "A revealable field.", "enum": [ "speed", "address", "movement_status", "connection_status", "driver_name", "driver_phone", "vehicle_label", "vehicle_reg_number" ] } }, "sensor_ids": { "type": [ "array", "null" ], "description": "Optional. IDs of the tracker's sensors whose readings the link shows.", "items": { "type": "integer" } }, "state_fields": { "type": [ "array", "null" ], "description": "Optional. Names of tracker state fields the link shows.", "items": { "type": "string" } }, "counters": { "type": [ "array", "null" ], "description": "Optional. Counter types the link shows.", "items": { "type": "string" } } } } } }, "LocationLinkParams": { "type": "object", "description": "What a geo link shows besides the trackers themselves, and how the map is presented.", "properties": { "bounding_zone_ids": { "type": "array", "description": "Geofences that bound where the link reports a position. At most 100.", "items": { "type": "integer" }, "maxItems": 100 }, "bounding_mode": { "type": [ "string", "null" ], "description": "How the bounding geofences apply: `inside` reports a position only within them, `outside` only beyond them. Optional when there are no bounding geofences.", "enum": [ "inside", "outside" ] }, "place_ids": { "type": [ "array", "null" ], "description": "Places to draw on the link's map. At most 100.", "items": { "type": "integer" }, "maxItems": 100 }, "place_custom_field_ids": { "type": [ "array", "null" ], "description": "Custom fields of those places to show. Each must be a custom field of the place entity type.", "items": { "type": "integer" } }, "zone_ids": { "type": [ "array", "null" ], "description": "Geofences to draw on the link's map. Distinct from `bounding_zone_ids`, which restrict reporting rather than decorate the map. At most 100.", "items": { "type": "integer" }, "maxItems": 100 }, "display_options": { "$ref": "#/components/schemas/LocationLinkDisplayOptions" } } }, "LocationLinkDisplayOptions": { "type": "object", "description": "How the shared map looks to the person opening the geo link.", "properties": { "map": { "type": "string", "description": "Base map to show, for example `roadmap`, `satellite`, `hybrid` or `osm`. Which values work depends on the maps available to the account, so the platform does not validate this." }, "autoscale": { "type": "boolean", "description": "Default `true`. Fit the map to the trackers shown." }, "show_icons": { "type": "boolean", "description": "Default `false`. Show tracker icons." }, "show_driver_info": { "type": "boolean", "description": "Default `false`. Show driver details." }, "show_vehicle_info": { "type": "boolean", "description": "Default `false`. Show vehicle details." }, "trace_duration": { "type": "integer", "description": "Default 5. How long, in seconds, the trailing trace behind each tracker remains visible. 0 shows no trace." } } }, "LocationLink": { "type": "object", "description": "A geo link: a shareable session showing the live location of chosen trackers to someone without a platform account.", "properties": { "id": { "type": "integer", "description": "Geo link ID. Read-only, assigned on creation, and required when updating." }, "hash": { "type": "string", "description": "The session hash that identifies the link to whoever it was shared with. Read-only." }, "create_date": { "type": "string", "description": "When the link was created, in `YYYY-MM-DD HH:MM:SS` format. Read-only." }, "creator_id": { "type": "integer", "description": "ID of the user who created the link. Read-only." }, "enabled": { "type": "boolean", "description": "Whether the link is currently active. Read-only here; change it with `tracker/location/link/status/change`." }, "description": { "type": "string", "description": "Description of the link. At most 255 printable characters, and may be empty.", "maxLength": 255 }, "lifetime": { "oneOf": [ { "$ref": "#/components/schemas/LocationLinkLifetime" }, { "type": "null" } ], "description": "When the link is valid, or `null` for no limit at either end." }, "trackers": { "type": "array", "description": "The trackers the link shows. 1 to 100 entries.", "items": { "$ref": "#/components/schemas/LocationLinkTracker" } }, "params": { "$ref": "#/components/schemas/LocationLinkParams" } } }, "StatusListing": { "type": "object", "description": "A named set of working statuses that can be assigned to trackers. Different trackers can carry different lists.", "properties": { "id": { "type": "integer", "description": "Status list ID. Read-only, assigned on creation, and required when updating.", "minimum": 1 }, "label": { "type": "string", "description": "Human-readable label. 1 to 200 printable characters, trimmed, and cannot be blank.", "minLength": 1, "maxLength": 200 }, "employee_controlled": { "type": "boolean", "description": "When `true`, employees may change their own working status, for example from the Mobile Tracker App." }, "supervisor_controlled": { "type": "boolean", "description": "When `true`, supervisors may change the status, for example from the monitoring interface. `status/tracker/assign` returns error 13 when this is `false`." }, "entries": { "type": "array", "description": "IDs of the working statuses in this list, **in display order**. Rejected on creation, required on update, and on update must be exactly the statuses already in the list.", "items": { "type": "integer" } } } }, "RoutePoint": { "type": "object", "description": "A geographic point on a route.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } }, "required": [ "lat", "lng" ] }, "RouteKeyPoint": { "type": "object", "description": "One of the points the caller asked for, located within the returned route.", "properties": { "id": { "type": "integer", "description": "Index of this point within the route's `list`." }, "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." }, "distance": { "type": "integer", "description": "Length of the route from the start up to this point, in metres. 0 at the start." }, "time": { "type": "integer", "description": "Duration of the route from the start up to this point, in seconds. 0 at the start." } } }, "TrackTotals": { "type": "object", "description": "Totals across every track returned.", "properties": { "count": { "type": "integer", "description": "Number of tracks." }, "length": { "type": "number", "description": "Sum of all track lengths, in kilometres." }, "trip_duration": { "type": "string", "description": "Total time spent moving, as an ISO 8601 duration." }, "parking_duration": { "type": "string", "description": "Total time spent stationary, as an ISO 8601 duration. Measured across the whole requested period, so it includes the gaps before the first track and after the last." }, "norm_fuel_consumed": { "type": "number", "description": "Fuel consumed across all tracks, in litres. Omitted entirely when no track carries a figure." } } }, "TagBinding": { "type": "object", "description": "A tag attached to a tracker.", "properties": { "tag_id": { "type": "integer", "description": "ID of the tag. Unique within a tracker.", "examples": [ 456789 ] }, "ordinal": { "type": "integer", "description": "Position or kind of the tag. Unique within a tracker, maximum 5.", "minimum": 1, "maximum": 5, "examples": [ 4 ] } }, "required": [ "tag_id", "ordinal" ] }, "TrackerSource": { "type": "object", "description": "The physical device behind a tracker.", "properties": { "id": { "type": "integer", "description": "Source ID.", "examples": [ 234567 ] }, "device_id": { "type": "string", "description": "Device ID, also called the source IMEI. Its accepted shape depends on the model's `id_type`.", "examples": [ "9999999988888" ] }, "model": { "type": "string", "description": "Model code, as returned by `tracker/list_models`.", "examples": [ "telfmb920" ] }, "blocked": { "type": "boolean", "description": "`true` when the tracker is blocked because its tariff ended." }, "tariff_id": { "type": "integer", "description": "ID of the tracker's tariff.", "examples": [ 345678 ] }, "status_listing_id": { "type": [ "integer", "null" ], "description": "ID of the working status list assigned to this tracker, or null." }, "creation_date": { "type": "string", "description": "Date the tracker was registered, `YYYY-MM-DD`.", "examples": [ "2011-09-21" ] }, "tariff_end_date": { "type": [ "string", "null" ], "description": "Date of the next tariff prolongation, `YYYY-MM-DD`, or null." }, "phone": { "type": [ "string", "null" ], "description": "Phone number of the device. Null or empty when the device has no GSM module, or uses a bundled SIM whose number is hidden from the user." } } }, "Tracker": { "type": "object", "description": "A tracking device registered in the platform. One of the central objects of this API.", "properties": { "id": { "type": "integer", "description": "Tracker ID, also known as the object ID.", "examples": [ 123456 ] }, "label": { "type": "string", "description": "Tracker label.", "examples": [ "tracker label" ] }, "clone": { "type": "boolean", "description": "`true` when this tracker is a clone of another." }, "group_id": { "type": "integer", "description": "Tracker group ID, `0` when the tracker is in no group.", "examples": [ 167 ] }, "avatar_file_name": { "type": "string", "description": "Avatar file name. Present only when the tracker has one." }, "source": { "$ref": "#/components/schemas/TrackerSource" }, "tag_bindings": { "type": "array", "description": "Tags attached to the tracker. Returned by `tracker/list`, and by the registration and replacement calls.", "items": { "$ref": "#/components/schemas/TagBinding" } } } }, "TrackerOutput": { "type": "object", "description": "A named digital output on a tracker.", "properties": { "number": { "type": "integer", "description": "Output number.", "examples": [ 1 ] }, "title": { "type": "string", "description": "User-defined output name.", "examples": [ "OUT1" ] } }, "required": [ "number", "title" ] }, "SensorValue": { "type": "object", "description": "The last reading of one sensor. Returned by the `get_fuel`, `get_readings` and `get_diagnostics` calls, which differ only in which sensors they select.", "properties": { "label": { "type": "string", "description": "Sensor label.", "examples": [ "Sensor #1" ] }, "name": { "type": "string", "description": "Name of the sensor's raw input. The full set is available from `tracker/sensor/input_name/list`.", "examples": [ "can_fuel" ] }, "type": { "type": "string", "description": "Type of quantity the sensor measures.", "examples": [ "fuel" ] }, "value": { "type": "number", "description": "Reading, in the units named by `units_type`.", "examples": [ 100 ] }, "units_type": { "type": "string", "description": "Unit of measurement of the sensor input.", "examples": [ "litre" ] }, "units": { "type": "string", "description": "User label for the sensor's units.", "examples": [ "litres" ] }, "converted_units_type": { "type": [ "string", "null" ], "description": "Unit of the measurement system the user prefers, from their account settings. Null when no conversion is needed, meaning `units_type` already belongs to that system." }, "converted_value": { "type": [ "number", "null" ], "description": "The reading in `converted_units_type`. Null when no conversion is needed." } } }, "TrackerInputState": { "type": "object", "description": "A semantic input bound to a digital input.", "properties": { "type": { "type": "string", "description": "One of the predefined semantic input types.", "enum": [ "ignition", "engine", "mass", "car_alarm", "sos_button", "hood", "door", "car_lock", "custom" ] }, "name": { "type": [ "string", "null" ], "description": "User-defined name for the semantic input, or null." }, "status": { "type": "boolean", "description": "`true` when the input is active." }, "input_number": { "type": "integer", "description": "Number of the associated discrete input.", "examples": [ 1 ] } } }, "TrackerInputInfo": { "type": "object", "description": "Digital and semantic input state for one tracker.", "properties": { "inputs": { "type": "array", "description": "State of every digital input. `[true, true, false]` means inputs 1 and 2 are on and input 3 is off.", "items": { "type": "boolean" } }, "states": { "type": "array", "description": "Semantic inputs bound to those digital inputs.", "items": { "$ref": "#/components/schemas/TrackerInputState" } }, "update_time": { "type": "string", "description": "When this data was last updated.", "examples": [ "2021-05-20 13:48:02" ] } } }, "TrackerState": { "type": "object", "description": "Current state of a tracker: GPS, GSM, inputs, outputs and movement.", "properties": { "source_id": { "type": "integer", "description": "Tracker data source ID.", "examples": [ 545139 ] }, "gps": { "type": "object", "description": "Last known GPS fix. Note that `lat` and `lng` sit inside `location`, not directly on this object.", "properties": { "updated": { "type": [ "string", "null" ], "description": "When the coordinates were last updated, in the account timezone, or null when never." }, "signal_level": { "type": [ "integer", "null" ], "description": "GPS signal level as a percentage, or null when the device cannot report it." }, "location": { "type": "object", "description": "The coordinates themselves.", "properties": { "lat": { "type": "number", "description": "Latitude." }, "lng": { "type": "number", "description": "Longitude." } } }, "heading": { "type": "integer", "description": "Direction bearing in degrees, 0 to 360." }, "speed": { "type": "integer", "description": "Speed in km/h." }, "alt": { "type": "integer", "description": "Altitude in metres." }, "precision": { "type": "integer", "description": "Precision in metres. Present only for some devices." }, "gsm_lbs": { "type": "boolean", "description": "`true` when the location came from GSM LBS rather than GPS. Present only when it applies." } } }, "connection_status": { "type": "string", "description": "Device connection status.", "enum": [ "signal_lost", "just_registered", "just_replaced", "offline", "idle", "active" ] }, "movement_status": { "type": "string", "description": "Movement status.", "enum": [ "moving", "stopped", "parked" ] }, "movement_status_update": { "type": [ "string", "null" ], "description": "When the movement status last changed, or null." }, "ignition": { "type": "boolean", "description": "State of the vehicle's ignition sensor, real or virtual. Present only for devices that report it." }, "ignition_update": { "type": "string", "description": "When the ignition state last changed. Present only alongside `ignition`." }, "gsm": { "type": "object", "description": "GSM status. Null for devices that cannot report it.", "properties": { "updated": { "type": [ "string", "null" ], "description": "When the GSM status was last updated, or null." }, "signal_level": { "type": [ "integer", "null" ], "description": "GSM signal level as a percentage, or null." }, "network_name": { "type": [ "string", "null" ], "description": "GSM network name, or null." }, "roaming": { "type": [ "boolean", "null" ], "description": "Roaming state, or null when the device cannot report it." } } }, "last_update": { "type": [ "string", "null" ], "description": "When the device state was last updated, or null." }, "battery_level": { "type": [ "integer", "null" ], "description": "Battery level as a percentage, or null when the device cannot report it." }, "battery_update": { "type": [ "string", "null" ], "description": "When the battery level was last updated, or null." }, "inputs": { "type": "array", "description": "State of every digital input.", "items": { "type": "boolean" } }, "inputs_update": { "type": [ "string", "null" ], "description": "When the inputs were last updated, or null." }, "outputs": { "type": "array", "description": "State of every digital output.", "items": { "type": "boolean" } }, "outputs_update": { "type": [ "string", "null" ], "description": "When the outputs were last updated, or null." }, "additional": { "type": "object", "description": "Additional state values. Which keys appear depends on the tracker model, and the object is empty for devices that report none. `hardware_key` is the last scanned driver identification key.", "additionalProperties": { "type": "object", "properties": { "value": { "type": "string", "description": "The value." }, "updated": { "type": "string", "description": "When it was last updated." } } } }, "actual_track_update": { "type": [ "string", "null" ], "description": "When the device last moved, meaning when its track was last extended." } } }, "TrackerModel": { "type": "object", "description": "An integrated tracker model and what it supports. Returned by `tracker/list_models`.", "properties": { "id": { "type": "integer", "description": "Model ID.", "examples": [ 2450 ] }, "vendor": { "type": "string", "description": "Vendor name.", "examples": [ "Navixy" ] }, "code": { "type": "string", "description": "Model text code, the value passed as `model` when registering.", "examples": [ "navixy_ngp" ] }, "parent_code": { "type": [ "string", "null" ], "description": "Code of the parent model, or null." }, "type": { "type": "string", "description": "Device category.", "enum": [ "logger", "portable", "vehicle", "personal" ] }, "name": { "type": "string", "description": "Human-readable model name.", "examples": [ "Navixy Generic Protocol" ] }, "id_type": { "type": "string", "description": "How the device is identified at registration. See the identifier types on the page.", "examples": [ "ascii,6,64" ] }, "id_namespace": { "type": "string", "description": "Namespace the identifier belongs to. Undocumented on the page, returned by the API." }, "has_phone": { "type": "boolean", "description": "`true` when the device has a phone number." }, "has_apn_settings": { "type": "boolean", "description": "`true` when the device has APN settings." }, "register": { "type": "boolean", "description": "`true` when the model is available for registration." }, "has_auto_registration": { "type": "boolean", "description": "`true` when the device can be registered by automatic commands from the platform." }, "check_bundle": { "type": "boolean", "description": "`true` when registration checks the device against a bundle. Undocumented on the page, returned by the API." }, "port": { "type": [ "integer", "null" ], "description": "Port to connect to on the tracking server. Null when the model supports none, or supports several, in which case read `connection`." }, "battery": { "type": "object", "description": "The device's internal battery, used to convert a raw reading into a percentage.", "properties": { "min_charge": { "type": "number", "description": "Minimum battery level." }, "low_charge": { "type": "number", "description": "Level at which the low-battery rule fires." }, "max_charge": { "type": "number", "description": "Maximum battery level." } } }, "altitude": { "type": "boolean", "description": "`true` when the device reports altitude." }, "satellites": { "type": "boolean", "description": "`true` when the device reports a satellite count." }, "gsm_level": { "type": "boolean", "description": "`true` when the device reports GSM signal strength." }, "gsm_network": { "type": "boolean", "description": "`true` when the device reports the GSM network name." }, "gsm_roaming": { "type": "boolean", "description": "`true` when the device reports roaming state." }, "has_detach_button": { "type": "boolean", "description": "`true` when the device has a detaching sensor." }, "has_fuel_input": { "type": "boolean", "description": "`true` when the device has a fuel sensor." }, "analog_inputs": { "type": "integer", "description": "Number of analog inputs." }, "digital_inputs": { "type": "integer", "description": "Number of digital inputs." }, "digital_outputs": { "type": "integer", "description": "Number of digital outputs." }, "rs232_inputs": { "type": "integer", "description": "Number of RS232 inputs." }, "track_control": { "type": "string", "description": "How track recording is controlled on the device. Undocumented on the page, returned by the API.", "examples": [ "none" ] }, "output_control": { "type": "string", "description": "How outputs are controlled.", "enum": [ "none", "default", "batch", "stateless", "async", "async_offline", "batch_async" ] }, "special_control": { "type": "string", "description": "Additional device-specific control types, comma separated when there are several.", "examples": [ "none" ] }, "multimedia": { "type": "boolean", "description": "`true` when the device supports multimedia. Undocumented on the page, returned by the API." }, "rules": { "type": "array", "description": "Rule types this model supports.", "items": { "type": "string" } }, "inputs": { "type": "array", "description": "Input types this model provides.", "items": { "type": "string" } }, "state_fields": { "type": "array", "description": "State fields this model reports.", "items": { "type": "string" } }, "special_settings": { "type": "array", "description": "Additional device-specific setting types.", "items": { "type": "string" } }, "sms_control": { "type": "array", "description": "SMS control commands this model supports.", "items": { "type": "string" } }, "connection": { "type": "array", "description": "Ways this model can connect to the platform. Present for models that offer more than a bare port.", "items": { "type": "object", "properties": { "protocol": { "type": "string", "description": "Application layer protocol.", "examples": [ "NGP" ] }, "transport": { "type": "string", "description": "Transport layer protocol.", "examples": [ "MQTTS" ] }, "url": { "type": "string", "description": "Full connection details: scheme, host, port and any credentials." }, "description": { "type": "string", "description": "Connection details in prose, such as the login and topic to use." } } } }, "has_led_control": { "type": "boolean", "description": "`true` when the device supports switching an LED." }, "has_location_request": { "type": "boolean", "description": "`true` when a location can be requested by SMS." }, "has_gprs_location_request": { "type": "boolean", "description": "`true` when a location can be requested over GPRS." }, "has_gsm_lbs_location_request": { "type": "boolean", "description": "`true` when a GSM LBS location can be requested over GPRS." }, "has_chat": { "type": "boolean", "description": "`true` when chat is available for this model." }, "has_custom_fields": { "type": "boolean", "description": "Default `false`. `true` when the model's protocol transmits field names, which allows a custom `input_name` to be set on its sensors." }, "has_odometer": { "type": "boolean", "description": "`true` when the device has an integrated odometer." }, "has_lbs": { "type": "boolean", "description": "`true` when the device sends cell information." }, "has_motion_sensor": { "type": "boolean", "description": "`true` when the device has an integrated motion sensor." }, "has_hardware_key": { "type": "boolean", "description": "`true` when the device can identify a driver by hardware key." }, "additional_fields": { "type": "array", "description": "Descriptions of the model-specific fields a user fills in at registration.", "items": { "type": "object" } } } }, "TrackerBaseSettings": { "type": "object", "description": "A tracker's label and group.", "properties": { "label": { "type": "string", "description": "User-defined label for the tracker.", "examples": [ "Courier" ] }, "group_id": { "type": "integer", "description": "Tracker group ID, `0` when the tracker is in no group.", "examples": [ 1 ] } } }, "SpecialSettings": { "type": "object", "description": "One special-settings object. Its `type` decides which further fields it carries, and the full catalogue of types is on the special settings page rather than here, because the set is long and model-specific.\n\nNote that `device_status_settings`, carrying `offline_timeout_seconds`, is returned by the API but is missing from that catalogue.", "properties": { "type": { "type": "string", "description": "Which kind of special settings this object holds, for example `electronic_lock_password` or `time_shift`.", "examples": [ "electronic_lock_password" ] } }, "required": [ "type" ], "additionalProperties": true }, "ChatMessage": { "type": "object", "description": "One chat message between a supervisor and a device.", "properties": { "id": { "type": "integer", "description": "Message ID.", "examples": [ 1 ] }, "submit_time": { "type": "string", "description": "When the message was submitted.", "examples": [ "2014-04-15 09:02:24" ] }, "update_time": { "type": [ "string", "null" ], "description": "Delivery time, for outgoing messages. Null until delivered." }, "text": { "type": "string", "description": "Message body.", "examples": [ "text of message" ] }, "type": { "type": "string", "description": "Direction of the message.", "enum": [ "INCOMING", "OUTGOING" ] }, "status": { "type": "string", "description": "Delivery status.", "enum": [ "PENDING", "DELIVERED" ] }, "employee_id": { "type": [ "integer", "null" ], "description": "Employee the message is associated with, or null." } } }, "TrackerCommand": { "type": "object", "description": "A reusable command defined on a tracker. The shape of `config` follows `type`: a `hardware` command sends a protocol string to the device, an `http` command sends a POST request to a URL.", "properties": { "id": { "type": "integer", "description": "Command ID, assigned by the server. Read-only.", "examples": [ 19 ] }, "name": { "type": "string", "description": "Human-readable label shown in the Commands block.", "examples": [ "Reboot" ] }, "type": { "type": "string", "description": "Which kind of command this is.", "enum": [ "hardware", "http" ] }, "config": { "description": "Configuration for the command, shaped by `type`.", "oneOf": [ { "type": "object", "title": "Hardware", "description": "Sent when `type` is `hardware`.", "properties": { "command": { "type": "string", "description": "The exact protocol-level string sent to the device. Valid values are device-specific; consult the manufacturer's documentation, because an incorrect string can have unintended effects.\n\nMay contain a single `<>` placeholder, whose value the caller supplies as `param` on execute.", "examples": [ "cpureset", "RELAY,<>#" ] }, "reliable": { "type": "boolean", "description": "When `true` the platform waits for the device to acknowledge delivery before marking the command sent." } } }, { "type": "object", "title": "HTTP", "description": "Sent when `type` is `http`.", "properties": { "url": { "type": "string", "description": "Endpoint that receives the POST request." }, "headers": { "type": "array", "description": "Headers to send. May be empty.", "items": { "type": "object", "properties": { "key": { "type": "string", "description": "Header name." }, "value": { "type": "string", "description": "Header value." } } } }, "body": { "type": "string", "description": "JSON payload for the POST body. `{{attribute_name}}` placeholders are replaced with live device values at execution time.\n\nMay also contain a single `<>` placeholder, which is a different mechanism: its value is supplied per call as `param` on execute rather than read from the device." } } } ] } } }, "Counter": { "type": "object", "description": "A tracker counter and how its raw source is scaled.", "properties": { "id": { "type": "integer", "description": "Counter ID.", "examples": [ 111 ] }, "type": { "type": "string", "description": "Which counter this is.", "enum": [ "odometer", "engine_hours" ] }, "multiplier": { "type": "number", "description": "Factor applied to the raw source value.", "examples": [ 1 ] } } }, "CounterValue": { "type": "object", "description": "The last known value of one counter.", "properties": { "type": { "type": "string", "description": "Which counter this is.", "enum": [ "odometer", "engine_hours" ] }, "value": { "type": "number", "description": "The counter value.", "examples": [ 100500.1 ] }, "update_time": { "type": "string", "description": "When the value was last updated.", "examples": [ "2014-03-06 13:57:00" ] } } }, "Contact": { "type": "object", "description": "Another user who shares tracker locations with this account.", "properties": { "user_id": { "type": "integer", "description": "ID of the user the contact is established with.", "examples": [ 12059 ] }, "first_name": { "type": "string", "description": "First name." }, "middle_name": { "type": "string", "description": "Middle name." }, "last_name": { "type": "string", "description": "Last name." }, "trackers": { "type": "array", "description": "The contact's trackers whose locations are shared with this account.", "items": { "$ref": "#/components/schemas/Tracker" } } } }, "TrackerGroup": { "type": "object", "description": "A tracker group. Groups organise trackers in the interface; the function is purely visual.", "properties": { "id": { "type": "integer", "description": "Group ID, assigned by the server. Read-only.", "examples": [ 167 ] }, "title": { "type": "string", "description": "User-specified group title.", "minLength": 1, "maxLength": 60, "examples": [ "Main office" ] }, "color": { "type": "string", "description": "Group colour in web format without a leading `#`. Sets the colour of the group's tracker markers on the map.", "examples": [ "FF6DDC" ] } } }, "RetranslatorBinding": { "type": "object", "description": "A binding between a tracker and a retranslator, which forwards the tracker's data to a third-party server.", "properties": { "retranslator_id": { "type": "integer", "description": "ID of the retranslator.", "examples": [ 4548 ] }, "fake_device_id": { "type": "string", "description": "Optional. When set, the retranslator forwards this value instead of the real device ID.", "examples": [ "AI568T" ] } } }, "SensorReading": { "type": "object", "description": "The last reading of one metering sensor.", "properties": { "sensor_id": { "type": "integer", "description": "Sensor ID.", "examples": [ 37641 ] }, "value": { "type": "number", "description": "The reading.", "examples": [ 5.66 ] }, "label": { "type": "string", "description": "Sensor label.", "examples": [ "Tank 1" ] }, "units": { "type": "string", "description": "Units the value is measured in.", "examples": [ "litres" ] }, "name": { "type": "string", "description": "Raw input name behind the sensor.", "examples": [ "fuel_level" ] }, "type": { "type": "string", "description": "Metering sensor type.", "examples": [ "fuel" ] }, "units_type": { "type": "string", "description": "Unit of measurement of the sensor input.", "examples": [ "litre" ] }, "update_time": { "type": "string", "description": "When the value was last updated." }, "min_value": { "type": "number", "description": "Lower bound of the sensor range. Present for sensors that declare one." }, "max_value": { "type": "number", "description": "Upper bound of the sensor range. Present for sensors that declare one." }, "converted_units_type": { "type": [ "string", "null" ], "description": "Units the value was converted into for the account's measurement system, or null when no conversion was needed." }, "converted_value": { "type": [ "number", "null" ], "description": "The converted value, or null when no conversion was needed." }, "composite_sensor_ids": { "type": "array", "description": "IDs of composite sensors this one feeds into. Present only when it feeds any.", "items": { "type": "integer" } } } }, "StateReading": { "type": "object", "description": "The last value of one state field.", "properties": { "field": { "type": "string", "description": "State field name.", "examples": [ "obd_mil_status" ] }, "value": { "description": "The value. Its type follows the field and may be string, number, boolean or null." }, "update_time": { "type": "string", "description": "When the value was last updated." } } }, "VirtualSensorReading": { "type": "object", "description": "The last reading of one virtual sensor.", "properties": { "sensor_id": { "type": "integer", "description": "Virtual sensor ID.", "examples": [ 37643 ] }, "label": { "type": "string", "description": "Sensor label.", "examples": [ "Virtual Ignition" ] }, "value": { "type": "string", "description": "The reading, always a string for virtual sensors.", "examples": [ "On" ] }, "type": { "type": "string", "description": "Virtual sensor type.", "examples": [ "virtual_ignition" ] }, "update_time": { "type": "string", "description": "When the value was last updated." } } }, "TrackerReadings": { "type": "object", "description": "Everything a tracker last reported: metering sensors, state fields, virtual sensors and counters.", "properties": { "inputs": { "type": "array", "description": "Metering sensor readings.", "items": { "$ref": "#/components/schemas/SensorReading" } }, "states": { "type": "array", "description": "State field values.", "items": { "$ref": "#/components/schemas/StateReading" } }, "virtual_sensors": { "type": "array", "description": "Virtual sensor readings.", "items": { "$ref": "#/components/schemas/VirtualSensorReading" } }, "counters": { "type": "array", "description": "Counter values.", "items": { "$ref": "#/components/schemas/CounterValue" } } } }, "RuleScheduleInterval": { "type": "object", "description": "One window during which a rule is active. A `weekly` interval repeats every week and uses weekday and time; a `fixed` interval runs once between two timestamps.", "properties": { "type": { "type": "string", "description": "Which kind of interval this is.", "enum": [ "weekly", "fixed" ] }, "from": { "description": "Start of the window. For `weekly`, an object with `weekday` (1 to 7) and `time`; for `fixed`, a timestamp string." }, "to": { "description": "End of the window, in the same shape as `from`." }, "interval_id": { "type": "integer", "description": "Interval ID, assigned by the server.", "examples": [ 48732 ] } } }, "Rule": { "type": "object", "description": "A rule: the conditions under which the platform logs an event and notifies someone.\n\n**Note the asymmetry on geofences.** Requests take `zone_ids`, an array; responses return `zone_id`, a single integer that is `0` when the rule is not bound to a geofence.", "properties": { "id": { "type": "integer", "description": "Rule ID.", "examples": [ 668054 ] }, "name": { "type": "string", "description": "Rule name.", "examples": [ "Lock is opened/closed" ] }, "type": { "type": "string", "description": "One of the predefined rule types. The rule types page lists them and the extra parameters each takes.", "examples": [ "locking_unlocking" ] }, "description": { "type": "string", "description": "Free-text description." }, "zone_id": { "type": "integer", "description": "Geofence the rule is bound to, or `0` when it is bound to none. **Responses only**; send `zone_ids` when writing.", "readOnly": true, "examples": [ 0 ] }, "zone_ids": { "type": "array", "description": "Geofences to bind the rule to. **Requests only**; responses return `zone_id` instead. Leave empty for a rule that works everywhere. Not allowed for the `offline` type, and required for `route` and `inoutzone`.", "items": { "type": "integer" }, "writeOnly": true, "examples": [ [ 18928 ] ] }, "trackers": { "type": "array", "description": "Trackers the rule applies to.", "items": { "type": "integer" } }, "primary_text": { "type": "string", "description": "Primary notification text." }, "secondary_text": { "type": "string", "description": "Secondary notification text." }, "param": { "type": "integer", "description": "A general-purpose integer parameter whose meaning follows `type`." }, "alerts": { "type": "object", "description": "Where notifications go.", "properties": { "sms_phones": { "type": "array", "description": "Phone numbers for SMS notifications.", "items": { "type": "string" } }, "phones": { "type": "array", "description": "Phone numbers for voice calls.", "items": { "type": "string" } }, "emails": { "type": "array", "description": "Email addresses for notifications.", "items": { "type": "string" } }, "push_enabled": { "type": "boolean", "description": "Whether push notifications are sent." }, "emergency": { "type": "boolean", "description": "Whether notifications are marked as emergency, with distinct colour and sound." } } }, "suspended": { "type": "boolean", "description": "`true` while the rule is paused." }, "auto_created": { "type": "boolean", "description": "`true` when the platform created the rule itself. Present only on such rules." }, "schedule": { "type": "array", "description": "Windows during which the rule is active. Omit for a rule that is always active.", "items": { "$ref": "#/components/schemas/RuleScheduleInterval" } }, "extended_params": { "type": "object", "description": "Parameters specific to this rule `type`. The rule types page documents them per type.", "additionalProperties": true } } }, "CalibrationPoint": { "type": "object", "description": "One point on a sensor's calibration curve, mapping a raw reading to a real-world value. An analogue fuel sensor, for instance, reports volts that have to become litres.", "properties": { "in": { "type": "number", "description": "Raw value from the sensor.", "examples": [ 0.7 ] }, "out": { "type": "number", "description": "Value it corresponds to.", "examples": [ 60 ] } }, "required": [ "in", "out" ] }, "Sensor": { "type": "object", "description": "A sensor on a tracker. `type` selects which of three shapes this is: a **metering** sensor reads a continuous quantity, a **discrete** input reads an on/off line, and a **virtual** sensor derives a value from another input rather than measuring anything itself.", "properties": { "type": { "type": "string", "description": "Which kind of sensor this is.", "enum": [ "metering", "discrete", "virtual" ] }, "id": { "type": "integer", "description": "Sensor ID.", "examples": [ 860250 ] }, "name": { "type": "string", "description": "Sensor name.", "maxLength": 100, "examples": [ "OBD Coolant temperature" ] }, "sensor_type": { "type": "string", "description": "The sensor's subtype, drawn from a different set for each `type`. Metering: `fuel`, `temperature`, `rpm`, `custom`, `fuel_consumption`, `instant_consumption`, `power`, `speed`, `flow_meter`, `acceleration`. Discrete: `ignition`, `sos_button`, `power`, `engine`, `car_alarm`, `door`, `charge`, `detach`, `custom`. Virtual: `virtual_ignition` or `state`.", "examples": [ "temperature" ] }, "input_name": { "type": "string", "description": "Source input field name. Metering and virtual sensors only.", "maxLength": 64, "examples": [ "obd_coolant_t" ] }, "input_number": { "type": "integer", "description": "Assigned input number. **Discrete inputs only.**", "minimum": 1, "maximum": 8, "examples": [ 4 ] }, "divider": { "type": "number", "description": "Divider applied to the raw value. Metering sensors only.", "examples": [ 1 ] }, "accuracy": { "type": "number", "description": "Rounding accuracy, in steps of 0.25. Metering sensors only.", "minimum": 0, "maximum": 100, "examples": [ 0 ] }, "units": { "type": "string", "description": "User label for the units." }, "units_type": { "type": "string", "description": "Unit of measurement. Metering sensors only.", "examples": [ "celsius" ] }, "group_type": { "type": [ "string", "null" ], "description": "Aggregation method for this metering sensor when grouped. `sum` adds the values of grouped sensors; `avg` averages them. Required (non-null) for composite sensors; defaults to `sum` for non-composite metering sensors. Only meaningful on sensors of `type` `metering`.", "enum": [ "sum", "avg" ] }, "custom_field_name": { "type": "boolean", "description": "Default `false`. Whether `input_name` is a value the user typed rather than one the protocol defines. Only meaningful when the tracker model reports `has_custom_fields`." }, "parameters": { "type": "object", "description": "Extra settings, which differ by `type`.\n\nMetering sensors take `parent_ids` and `volume` for composites, `min` and `max` bounds on the raw value, `max_lowering_by_time` and `max_lowering_by_mileage` to cap legitimate drops, and `ignore_drains_in_move`, `ignore_refuels_in_move` and `refuel_gap_minutes` to control fuel event detection during movement.\n\nVirtual sensors take `calc_method`, one of `in_range`, `identity` or `bit_index`; `range_from` and `range_to` for `in_range`, at least one of which is required; `bit_index` for `bit_index`; and up to 100 `value_titles`, each a unique `value` with a display `title`.", "additionalProperties": true } }, "required": [ "type" ] }, "SensorInputName": { "type": "object", "description": "One sensor input or state field, with its description translated into the account's language.", "properties": { "input_name": { "type": "string", "description": "The identifier, the same value a sensor's `input_name` takes.", "examples": [ "acceleration" ] }, "description": { "type": "string", "description": "Human-readable description in the account's language.", "examples": [ "Acceleration" ] } } }, "SensorDataPoint": { "type": "object", "description": "One recorded sensor reading.", "properties": { "value": { "description": "The reading. Double, integer or string depending on the sensor.", "examples": [ 100500 ] }, "get_time": { "type": "string", "description": "When the value was received.", "examples": [ "2022-02-28 00:00:00" ] } } } } } }