{
 "openapi": "3.0.3",
 "servers": [
  {
   "url": "https://videoapi.glonassservice.ru"
  }
 ],
 "info": {
  "title": "Видеосервер API",
  "version": "1.0.0",
  "description": "Внешний API, совместимый по форме с Gelios API (/api/v1, items + paginationMetadata, OAuth2 password), плюс раздел /api/v1/video. Авторизация: токен входа или постоянный API-ключ (vsk_…) в заголовке Authorization: Bearer. Руководство — https://docs.glonassservice.ru/api/."
 },
 "paths": {
  "/api/v1/auth/login": {
   "post": {
    "tags": [
     "Авторизация"
    ],
    "summary": "Вход по логину и паролю (OAuth2 password, форма). Возвращает пару токенов.",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Tokens"
        }
       }
      }
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/x-www-form-urlencoded": {
       "schema": {
        "type": "object",
        "properties": {
         "grant_type": {
          "type": "string"
         },
         "username": {
          "type": "string"
         },
         "password": {
          "type": "string"
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/auth/refresh": {
   "post": {
    "tags": [
     "Авторизация"
    ],
    "summary": "Обновить токены по refresh_token.",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Tokens"
        }
       }
      }
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "example": {
         "refresh_token": ""
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/auth/logout": {
   "post": {
    "tags": [
     "Авторизация"
    ],
    "summary": "Выход (токены не хранятся на сервере; ответ всегда успешный).",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/auth": {
   "get": {
    "tags": [
     "Авторизация"
    ],
    "summary": "Кто я: пользователь, роль, компания (с ИНН), вошли ли по ключу, можно ли писать.",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Me"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/auth/keys": {
   "get": {
    "tags": [
     "Авторизация"
    ],
    "summary": "Мои API-ключи (сам ключ не показывается).",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   },
   "post": {
    "tags": [
     "Авторизация"
    ],
    "summary": "Выпустить API-ключ (только по токену входа). Ключ показывается один раз.",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ApiKeyCreated"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "example": {
         "name": "интеграция CRM",
         "write": false
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/auth/keys/{key_id}": {
   "delete": {
    "tags": [
     "Авторизация"
    ],
    "summary": "Отозвать ключ.",
    "parameters": [
     {
      "name": "key_id",
      "in": "path",
      "required": true,
      "description": "",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/companies": {
   "get": {
    "tags": [
     "Компании"
    ],
    "summary": "Компании, видимые пользователю (администратор — все): название и ИНН.",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/units": {
   "get": {
    "tags": [
     "Объекты"
    ],
    "summary": "Список доступных объектов. В каждом объекте creator — верхний управляющий компании-владельца (id совпадает с id пользователя), плюс поля модели Gelios: imei, hwManufacturer, hwType, isBlock, removed, phone, unitType, currentUserAccess.",
    "parameters": [
     {
      "name": "unit_ids",
      "in": "query",
      "required": false,
      "description": "Номера через запятую (параметр можно повторять)",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "fun",
      "in": "query",
      "required": false,
      "description": "Часть названия",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "fim",
      "in": "query",
      "required": false,
      "description": "Часть номера терминала",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "fgi",
      "in": "query",
      "required": false,
      "description": "Только группа",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "incllsv",
      "in": "query",
      "required": false,
      "description": "Приложить последнее сообщение (lastMsg)",
      "schema": {
       "type": "boolean"
      }
     },
     {
      "name": "pl",
      "in": "query",
      "required": false,
      "description": "Размер страницы, 1–1000 (по умолчанию 100)",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "po",
      "in": "query",
      "required": false,
      "description": "Смещение",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "items": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Unit"
           }
          },
          "paginationMetadata": {
           "$ref": "#/components/schemas/PaginationMetadata"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/units/{unit_id}": {
   "get": {
    "tags": [
     "Объекты"
    ],
    "summary": "Объект.",
    "parameters": [
     {
      "name": "unit_id",
      "in": "path",
      "required": true,
      "description": "Номер терминала (строка: может начинаться с нуля)",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Unit"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/units/last-msgs/full": {
   "post": {
    "tags": [
     "Объекты"
    ],
    "summary": "Последние сообщения (pure и raw) по списку объектов; пустой список — по всем доступным.",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "items": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/LastMsgFull"
           }
          },
          "paginationMetadata": {
           "$ref": "#/components/schemas/PaginationMetadata"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "example": {
         "unitIds": [
          "0120040"
         ]
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/units/{unit_id}/last-msg/pure": {
   "get": {
    "tags": [
     "Объекты"
    ],
    "summary": "Последнее сообщение объекта (нормализованное).",
    "parameters": [
     {
      "name": "unit_id",
      "in": "path",
      "required": true,
      "description": "Номер терминала (строка: может начинаться с нуля)",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LastMsg"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/units/{unit_id}/last-msg/raw": {
   "get": {
    "tags": [
     "Объекты"
    ],
    "summary": "Последнее сообщение объекта как его хранит навигация.",
    "parameters": [
     {
      "name": "unit_id",
      "in": "path",
      "required": true,
      "description": "Номер терминала (строка: может начинаться с нуля)",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/storage/messages": {
   "get": {
    "tags": [
     "История"
    ],
    "summary": "Сообщения (точки) объекта за период, до 93 суток.",
    "parameters": [
     {
      "name": "unit_id",
      "in": "query",
      "required": true,
      "description": "",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "time_from",
      "in": "query",
      "required": true,
      "description": "Unix-секунды",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "time_to",
      "in": "query",
      "required": true,
      "description": "Unix-секунды",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "columns",
      "in": "query",
      "required": false,
      "description": "Какие поля оставить, через запятую (time всегда)",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "return_type",
      "in": "query",
      "required": false,
      "description": "json (по умолчанию) или csv",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Не более точек, по умолчанию 10000, максимум 50000",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/units/{unit_id}/commands/send": {
   "post": {
    "tags": [
     "Команды"
    ],
    "summary": "Отправить текстовую команду регистратору (только администратор, ключ с правом записи).",
    "parameters": [
     {
      "name": "unit_id",
      "in": "path",
      "required": true,
      "description": "Номер терминала (строка: может начинаться с нуля)",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/CommandTask"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "example": {
         "text": "",
         "type": "tcp"
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/units/{unit_id}/commands/history": {
   "get": {
    "tags": [
     "Команды"
    ],
    "summary": "История команд, отправленных через API.",
    "parameters": [
     {
      "name": "unit_id",
      "in": "path",
      "required": true,
      "description": "Номер терминала (строка: может начинаться с нуля)",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "pl",
      "in": "query",
      "required": false,
      "description": "Размер страницы, 1–1000 (по умолчанию 100)",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "po",
      "in": "query",
      "required": false,
      "description": "Смещение",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/video/units": {
   "get": {
    "tags": [
     "Видео"
    ],
    "summary": "Видео по всем доступным объектам: каналы, наличие видео, глубина архива, ошибки.",
    "parameters": [
     {
      "name": "hasErrors",
      "in": "query",
      "required": false,
      "description": "",
      "schema": {
       "type": "boolean"
      }
     },
     {
      "name": "videoPresent",
      "in": "query",
      "required": false,
      "description": "",
      "schema": {
       "type": "boolean"
      }
     },
     {
      "name": "minArchiveDepth",
      "in": "query",
      "required": false,
      "description": "Не менее суток архива",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "pl",
      "in": "query",
      "required": false,
      "description": "Размер страницы, 1–1000 (по умолчанию 100)",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "po",
      "in": "query",
      "required": false,
      "description": "Смещение",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "items": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/VideoStatus"
           }
          },
          "paginationMetadata": {
           "$ref": "#/components/schemas/PaginationMetadata"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/video/units/{unit_id}/status": {
   "get": {
    "tags": [
     "Видео"
    ],
    "summary": "Сводка видео по объекту.",
    "parameters": [
     {
      "name": "unit_id",
      "in": "path",
      "required": true,
      "description": "Номер терминала (строка: может начинаться с нуля)",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/VideoStatus"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/video/units/{unit_id}/channels": {
   "get": {
    "tags": [
     "Видео"
    ],
    "summary": "То же, плюс список каналов (channels).",
    "parameters": [
     {
      "name": "unit_id",
      "in": "path",
      "required": true,
      "description": "Номер терминала (строка: может начинаться с нуля)",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/VideoStatus"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/video/units/{unit_id}/channels/{channel}/live": {
   "post": {
    "tags": [
     "Видео"
    ],
    "summary": "Запросить видеопоток: возвращает короткоживущие ссылки wsUrl (WebSocket), httpUrl (сырой H.264 по HTTP) и snapshotUrl. Действуют 5 минут на подключение; ссылки можно отдавать плееру.",
    "parameters": [
     {
      "name": "unit_id",
      "in": "path",
      "required": true,
      "description": "Номер терминала (строка: может начинаться с нуля)",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "channel",
      "in": "path",
      "required": true,
      "description": "Номер канала с 1",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "stream",
      "in": "query",
      "required": false,
      "description": "sub (по умолчанию) или main",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   },
   "get": {
    "tags": [
     "Видео"
    ],
    "summary": "Живое видео по заголовку Authorization: WebSocket (кадры: 2 байта длительности мс + H.264 Annex B) или, без Upgrade, непрерывный поток H.264 (video/h264).",
    "parameters": [
     {
      "name": "unit_id",
      "in": "path",
      "required": true,
      "description": "Номер терминала (строка: может начинаться с нуля)",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "channel",
      "in": "path",
      "required": true,
      "description": "Номер канала с 1",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "stream",
      "in": "query",
      "required": false,
      "description": "sub (по умолчанию) или main",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/video/units/{unit_id}/channels/{channel}/snapshot": {
   "get": {
    "tags": [
     "Видео"
    ],
    "summary": "Снимок канала (JPEG) из живого потока; регистратор должен быть на связи, ответ до ~25 секунд.",
    "parameters": [
     {
      "name": "unit_id",
      "in": "path",
      "required": true,
      "description": "Номер терминала (строка: может начинаться с нуля)",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "channel",
      "in": "path",
      "required": true,
      "description": "Номер канала с 1",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "stream",
      "in": "query",
      "required": false,
      "description": "sub (по умолчанию) или main",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/video/live/{ticket}": {
   "get": {
    "tags": [
     "Видео"
    ],
    "summary": "Поток по ссылке из ответа на запрос видеопотока (без заголовка Authorization).",
    "parameters": [
     {
      "name": "ticket",
      "in": "path",
      "required": true,
      "description": "",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/video/snapshot/{ticket}": {
   "get": {
    "tags": [
     "Видео"
    ],
    "summary": "Снимок по ссылке из ответа на запрос видеопотока (без заголовка Authorization).",
    "parameters": [
     {
      "name": "ticket",
      "in": "path",
      "required": true,
      "description": "",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/users": {
   "get": {
    "tags": [
     "Пользователи"
    ],
    "summary": "Пользователи, доступные вызывающему (администратор — все, управляющий — своя компания).",
    "parameters": [
     {
      "name": "fusid",
      "in": "query",
      "required": false,
      "description": "Номера пользователей через запятую",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "fusl",
      "in": "query",
      "required": false,
      "description": "Часть логина",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "fuse",
      "in": "query",
      "required": false,
      "description": "Часть email",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "flnl",
      "in": "query",
      "required": false,
      "description": "Часть названия (legalName)",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "fusph",
      "in": "query",
      "required": false,
      "description": "Часть телефона",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "fcrid",
      "in": "query",
      "required": false,
      "description": "Только созданные этим пользователем",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "fisa",
      "in": "query",
      "required": false,
      "description": "Только администраторы/управляющие",
      "schema": {
       "type": "boolean"
      }
     },
     {
      "name": "fisbl",
      "in": "query",
      "required": false,
      "description": "Только заблокированные",
      "schema": {
       "type": "boolean"
      }
     },
     {
      "name": "pl",
      "in": "query",
      "required": false,
      "description": "Размер страницы, 1–1000 (по умолчанию 100)",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "po",
      "in": "query",
      "required": false,
      "description": "Смещение",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   },
   "post": {
    "tags": [
     "Пользователи"
    ],
    "summary": "Создать пользователя (только администратор, право записи). Если creatorId — администратор, создаётся клиент: компания (по legalName, иначе по логину; такая уже есть — пользователь добавится в неё) с первой группой объектов. Если creatorId — сотрудник компании, пользователь добавляется в его компанию. isAdmin=true — роль «управляющий», false — «пользователь» с доступом к корневым группам компании. Необязательное поле inn — ИНН компании.",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "example": {
         "login": "client1",
         "password": "secret1",
         "creatorId": 1,
         "isAdmin": true,
         "legalName": "ООО Ромашка",
         "email": "",
         "phone": "",
         "isBlock": false,
         "inn": ""
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/users/{user_id}": {
   "get": {
    "tags": [
     "Пользователи"
    ],
    "summary": "Пользователь.",
    "parameters": [
     {
      "name": "user_id",
      "in": "path",
      "required": true,
      "description": "Номер пользователя",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   },
   "patch": {
    "tags": [
     "Пользователи"
    ],
    "summary": "Изменить пользователя: legalName, email, phone, login, password, isBlock, isAdmin (только администратор, право записи).",
    "parameters": [
     {
      "name": "user_id",
      "in": "path",
      "required": true,
      "description": "Номер пользователя",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "example": {
         "legalName": "",
         "email": "",
         "phone": "",
         "isBlock": false,
         "password": ""
        }
       }
      }
     }
    }
   },
   "delete": {
    "tags": [
     "Пользователи"
    ],
    "summary": "Удалить пользователя (нельзя себя и администраторов).",
    "parameters": [
     {
      "name": "user_id",
      "in": "path",
      "required": true,
      "description": "Номер пользователя",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/users/{user_id}/accesses/units": {
   "get": {
    "tags": [
     "Пользователи"
    ],
    "summary": "Объекты, доступные пользователю.",
    "parameters": [
     {
      "name": "user_id",
      "in": "path",
      "required": true,
      "description": "Номер пользователя",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "pl",
      "in": "query",
      "required": false,
      "description": "Размер страницы, 1–1000 (по умолчанию 100)",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "po",
      "in": "query",
      "required": false,
      "description": "Смещение",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/users/{user_id}/accesses/units/groups": {
   "get": {
    "tags": [
     "Пользователи"
    ],
    "summary": "Группы объектов компании пользователя с уровнем его доступа (0 — нет).",
    "parameters": [
     {
      "name": "user_id",
      "in": "path",
      "required": true,
      "description": "Номер пользователя",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "pl",
      "in": "query",
      "required": false,
      "description": "Размер страницы, 1–1000 (по умолчанию 100)",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "po",
      "in": "query",
      "required": false,
      "description": "Смещение",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ]
   }
  },
  "/api/v1/users/{user_id}/accesses/units/groups/{group_id}": {
   "put": {
    "tags": [
     "Пользователи"
    ],
    "summary": "Дать или забрать доступ пользователя (role=user) к группе объектов: newAccess 0 — забрать, 1/3/7 — дать.",
    "parameters": [
     {
      "name": "user_id",
      "in": "path",
      "required": true,
      "description": "Номер пользователя",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "group_id",
      "in": "path",
      "required": true,
      "description": "Номер группы",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Успешно",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Не авторизован"
     }
    },
    "security": [
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "example": {
         "newAccess": 1
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "PaginationMetadata": {
    "type": "object",
    "properties": {
     "totalCount": {
      "type": "integer"
     },
     "startRow": {
      "type": "integer"
     },
     "endRow": {
      "type": "integer"
     }
    }
   },
   "Tokens": {
    "type": "object",
    "properties": {
     "access_token": {
      "type": "string"
     },
     "refresh_token": {
      "type": "string"
     },
     "expires_in": {
      "type": "integer",
      "description": "Момент истечения access-токена, unix-секунды (как у Gelios)"
     },
     "token_type": {
      "type": "string"
     }
    }
   },
   "Me": {
    "type": "object",
    "properties": {
     "id": {
      "type": "integer"
     },
     "login": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "role": {
      "type": "string",
      "enum": [
       "admin",
       "manager",
       "user"
      ]
     },
     "companyId": {
      "type": "integer",
      "nullable": true
     },
     "companyName": {
      "type": "string",
      "nullable": true
     },
     "companyInn": {
      "type": "string",
      "nullable": true,
      "description": "ИНН компании"
     },
     "viaApiKey": {
      "type": "boolean"
     },
     "canWrite": {
      "type": "boolean"
     }
    }
   },
   "ApiKeyCreated": {
    "type": "object",
    "properties": {
     "id": {
      "type": "integer"
     },
     "key": {
      "type": "string",
      "description": "Показывается один раз"
     },
     "name": {
      "type": "string"
     },
     "prefix": {
      "type": "string"
     },
     "write": {
      "type": "boolean"
     }
    }
   },
   "Unit": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "plate": {
      "type": "string"
     },
     "manufacturer": {
      "type": "string"
     },
     "model": {
      "type": "string"
     },
     "iconType": {
      "type": "string"
     },
     "groupId": {
      "type": "integer",
      "nullable": true
     },
     "groupName": {
      "type": "string",
      "nullable": true
     },
     "companyId": {
      "type": "integer",
      "nullable": true
     },
     "channelsCount": {
      "type": "integer",
      "description": "Количество видеоканалов"
     },
     "online": {
      "type": "boolean"
     },
     "lastSeen": {
      "type": "integer",
      "nullable": true,
      "description": "Unix-секунды"
     },
     "lastMsg": {
      "$ref": "#/components/schemas/LastMsg"
     }
    }
   },
   "LastMsg": {
    "type": "object",
    "properties": {
     "unitId": {
      "type": "string"
     },
     "time": {
      "type": "number",
      "description": "Время точки, unix-секунды"
     },
     "receivedAt": {
      "type": "number"
     },
     "latitude": {
      "type": "number"
     },
     "longitude": {
      "type": "number"
     },
     "speed": {
      "type": "number",
      "description": "км/ч"
     },
     "course": {
      "type": "integer"
     },
     "mileage": {
      "type": "number",
      "nullable": true,
      "description": "км"
     },
     "fuel": {
      "type": "number",
      "nullable": true
     },
     "ignition": {
      "type": "boolean",
      "nullable": true
     },
     "satellites": {
      "type": "integer",
      "nullable": true
     },
     "valid": {
      "type": "boolean",
      "nullable": true,
      "description": "Есть фикс GNSS"
     },
     "alarms": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "address": {
      "type": "string",
      "nullable": true
     }
    }
   },
   "LastMsgFull": {
    "type": "object",
    "properties": {
     "unitId": {
      "type": "string"
     },
     "pure": {
      "$ref": "#/components/schemas/LastMsg"
     },
     "raw": {
      "type": "object"
     }
    }
   },
   "CommandTask": {
    "type": "object",
    "properties": {
     "taskId": {
      "type": "integer"
     },
     "historyRowId": {
      "type": "integer"
     },
     "status": {
      "type": "string",
      "enum": [
       "sent"
      ]
     }
    }
   },
   "VideoError": {
    "type": "object",
    "properties": {
     "code": {
      "type": "string",
      "enum": [
       "device_offline",
       "camera_fault",
       "archive_empty",
       "archive_unknown"
      ]
     },
     "message": {
      "type": "string"
     },
     "channel": {
      "type": "integer",
      "nullable": true
     }
    }
   },
   "VideoStatus": {
    "type": "object",
    "properties": {
     "unitId": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "online": {
      "type": "boolean"
     },
     "channelsCount": {
      "type": "integer",
      "description": "Количество видеоканалов"
     },
     "channelsActive": {
      "type": "integer",
      "description": "Каналов, отдающих поток сейчас"
     },
     "videoPresent": {
      "type": "boolean",
      "nullable": true,
      "description": "true — поток идёт; false — регистратор не на связи или сообщил о неисправности камеры; null — на связи, поток не открыт"
     },
     "archive": {
      "type": "object",
      "properties": {
       "known": {
        "type": "boolean"
       },
       "depthDays": {
        "type": "integer",
        "nullable": true
       },
       "depthFrom": {
        "type": "string",
        "nullable": true,
        "description": "Самые ранние сутки с записью, ГГГГ-ММ-ДД"
       },
       "checkedAt": {
        "type": "integer",
        "nullable": true
       }
      }
     },
     "errors": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/VideoError"
      }
     },
     "channels": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "channel": {
         "type": "integer"
        },
        "streaming": {
         "type": "boolean"
        },
        "videoPresent": {
         "type": "boolean",
         "nullable": true
        },
        "archiveDepthDays": {
         "type": "integer",
         "nullable": true
        },
        "archiveDepthFrom": {
         "type": "string",
         "nullable": true
        },
        "errors": {
         "type": "array",
         "items": {
          "$ref": "#/components/schemas/VideoError"
         }
        }
       }
      }
     }
    }
   }
  },
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "Токен входа или API-ключ vsk_…"
   }
  }
 }
}