Для разработчиков

API Интеграма

Через API доступно всё, что пользователь делает в интерфейсе Интеграма: создать таблицы и колонки, завести записи, пользователей и роли, выдать доступы, построить запрос с фильтрами и формулами. Этими же командами пользуется сборщик приложений excel-to-app.

Примеры даны для https://ideav.ru и базы mydb: подставьте адрес своей установки и имя базы.

Общие правила

Термины

Всё в базе Интеграма — термины: Клиент, Адрес, Статус, Пользователь. Пользователь видит их как таблицы и колонки, разработчик — как типы и их реквизиты. «Таблица Клиент с колонками Адрес и Телефон» и «тип Клиент с реквизитами Адрес и Телефон» — одно и то же.

  • Таблица (тип) — термин верхнего уровня. Значение её первой колонки — главное значение записи.
  • Колонка (реквизит) — термин, добавленный к таблице. Один термин может быть колонкой у разных таблиц.
  • Справочная колонка — ссылка на другую таблицу; в ней хранится id записи справочника.
  • Подчинённая таблица — записи, принадлежащие записи родителя (позиции заказа, пункты меню роли).

Адрес команды

Команда — это адрес вида /{база}/{команда}/{id}. Параметр JSON в адресе говорит, что ответ нужен в JSON, а не HTML-страницей.

https://ideav.ru/{база}/{команда}/{id}?JSON=1
  • _d_* — изменение структуры (таблиц и колонок), POST;
  • _m_* — изменение данных (записей), POST;
  • metadata, object, report, dict, xsrf — чтение, GET.

Поля POST-запросов передаются формой (-F в curl или application/x-www-form-urlencoded). Значение колонки передаётся полем t{id колонки}, например t41=ivan@example.com.

Имена вместо кодов

Таблицы, записи, колонки, роли и запросы можно называть по именам, не зная их id. Структуру целиком — таблицы, ссылки, роли с правами и отчёты — описывает по именам одна команда _d_schema.

ГдеКакПример
Таблица в адресеИмя таблицы вместо её id — в любой командеmetadata/Клиент, object/Клиент, _m_new/Клиент
Запись в адресеИмя таблицы и главное значение записиedit_obj/Клиент/АО Ромашка, _m_set/Роль/Менеджер
Запрос в адресеНазвание запроса вместо idreport/Клиенты по категориям
Поля записиfields с именами колонок в _m_new, _m_set, _m_batchfields={"ИНН":"7701234567","Категория":"Лид"}
Чтение записейNAMED — поля ключами-именамиobject/Клиент?JSON_OBJ&NAMED
Структура и права_d_schema — таблицы, роли и отчёты по именамсм. «Схема одним запросом»
Фильтр записейИмя колонки вместо id; имя таблицы — фильтр по первой колонкеF_ИНН=7701234567, F_Клиент=%25Ромашка%25
Фильтр запросаИмя колонки в отчёте, пробелы — _FR_Дата_заказа=01.10.2026
curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/metadata/Пользователь?JSON=1"
curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/object/Роль?JSON_OBJ&F_Роль=admin"
curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/edit_obj/Роль/admin?JSON=1"
curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/report/Клиенты%20по%20категориям?JSON_KV"
  • пробел в адресе пишется как %20, остальную кириллицу curl передаёт как есть;
  • в адресе регистр не важен: report/intReports и report/intreports — один запрос; имя колонки в фильтре F_ пишется точно как в метаданных;
  • в fields регистр имени колонки не важен; неизвестное имя — ошибка 400 со списком полей таблицы;
  • если в базе два термина с одним именем, адрес берёт первый найденный — такие таблицы читайте по id.

Имена и коды смешиваются свободно: в fields рядом с именами работают прежние ключи t{id}, а id всего, что описано через _d_schema, приходят в ответе — для фильтров F_{id} и старых клиентов.

Ответы и ошибки

Команды _d_* и _m_* отвечают объектом одного вида: id — созданный или изменённый элемент, obj — объект, к которому он относится, warnings — предупреждения.

{"id": 643, "obj": 641, "next_act": "edit_types", "args": "ext", "warnings": ""}

Ошибка приходит массивом с ключом error и HTTP-статусом: 400 — неверные данные или устаревший _xsrf, 401 — нет авторизации, 403 — нет доступа, 404 — нет базы, таблицы или записи, 409 — на удаляемую запись есть ссылки.

[{"error": "Вы хотите удалить реквизит у типа при наличии этого реквизита у экземпляров (всего: 1)!"}]

Авторизация

Токен API

Для программ и интеграций удобнее всего постоянный токен: заведите в базе пользователя (например, api), впишите ему в поле «Токен» случайную строку из 15–64 символов и выдайте роль с нужными правами. Токен передаётся заголовком X-Authorization в каждом запросе.

TOKEN=a3f1c9...           # токен пользователя api
curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/metadata?JSON=1"

Заголовок читается, только если в адресе или теле запроса есть параметр JSON (JSON, JSON_KV, JSON_OBJ…). Без него запрос считается переходом браузера и уходит на страницу входа.

Вместо X-Authorization подходит Authorization: <токен> или Authorization: Bearer <токен>. Ещё токен можно передать кукой idb_{база} или полем формы token в POST-запросе. Кука нужна командам, работающим с сессией, например файловому менеджеру dir_admin.

curl -s -b "idb_mydb=$TOKEN" "https://ideav.ru/mydb/metadata?JSON=1"

Простейший вариант — Basic-авторизация: логин и пароль в каждом запросе. По RFC 7617 пара логин:пароль кодируется в Base64, curl делает это сам ключом -u.

curl -s -u "manager:Str0ng-Pass" "https://ideav.ru/mydb/metadata?JSON=1"

Токен XSRF

Каждый POST-запрос на изменение (_d_*, _m_*) должен нести поле _xsrf. Его выдаёт команда xsrf; заодно она сообщает пользователя, его роль и id.

curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/xsrf?JSON=1"

Ответ

{"_xsrf": "cd4b6417a3940ed4a84h074d", "token": "a3f1c9...", "user": "api", "role": "api", "id": "632", "msg": ""}

Получить в переменную (нужен jq)

XSRF=$(curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/xsrf?JSON=1" | jq -r ._xsrf)

Дальше в примерах $TOKEN и $XSRF — эти два значения. _xsrf привязан к токену, которым его получили: запрашивайте его и шлите POST под одним и тем же токеном.

Вход по логину и паролю

Команда auth проверяет логин и пароль и открывает сессию этого устройства. В ответе — токен сессии и _xsrf к нему.

curl -s "https://ideav.ru/mydb/auth?JSON=1" -F "login=manager" -F "pwd=Str0ng-Pass"

Ответ

{"_xsrf": "1cb413634d074d3804ed48", "token": "s.1824537600.9e0c...", "id": "632", "msg": ""}

Сессия живёт, пока ею пользуются: без запросов она гаснет через 30 дней. Пять неверных паролей подряд блокируют вход на 15 минут, сервер отвечает 429 с заголовком Retry-After.

Выход — GET /{база}/exit гасит текущую сессию, exit?all=1 — все сессии пользователя.

Базы

Список баз

Базы пользователя перечислены в его личном кабинете — базе my. Список отдаёт отчёт 313.

curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/my/report/313?JSON_KV=1"

Ответ (сокращён)

[
  {"DB": "test", "DBID": "178652", "Template": "RU", "Plan": "Startup", "Plan date": "23.04.2026"},
  {"DB": "mydb", "DBID": "195822", "Template": "RU", "Plan": "Startup", "Plan date": "23.04.2026"}
]

Создать базу

Команда _new_db кабинета my: db — имя новой базы (латиница и цифры), template — шаблон, обычно RU.

curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/my/_new_db/?JSON=1&db=apiman&template=RU"

Ответ

{"status": "Ok", "id": 221756}

В новой базе уже есть администратор: пользователь с логином, равным имени базы, и ролью admin (145). У него полный доступ в обход прав; переименовать его или сменить ему роль нельзя.

Структура: таблицы и колонки

Базовые типы

Тип каждой колонки — один из базовых. Его код передаётся параметром t при создании термина.

КодТипЧто хранит
3SHORTКороткая строка (до 127 символов)
8CHARSСтрока без ограничения длины
12MEMOМногострочный текст
13NUMBERЦелое число
14SIGNEDЧисло с дробной частью
9DATEДата, 30.10.2026
4DATETIMEДата и время; через API — unix-время в секундах
11BOOLEANДа / нет: 1 или пусто
10FILEФайл
2HTMLHTML-текст, выводится без экранирования
6PWDПароль: хранится хешем, на экране — «******»
7BUTTONКнопка-действие над записью
5GRANTОбъект системы — выбор таблицы или колонки
16REPORT_COLUMNКолонка запроса
17PATHПуть к файлу

Метаданные

Команда metadata без id описывает всю базу, с id — одну таблицу. Отсюда берут id колонок для t{id} в записях и запросах.

curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/metadata/18?JSON=1"
curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/metadata/Пользователь?JSON=1"     # то же по имени

Ответ (сокращён): таблица Пользователь

{
  "id": "18", "up": "0", "type": "3", "val": "Пользователь", "unique": "1",
  "reqs": [
    {"num": 1, "id": "115", "val": "Роль", "type": "3", "ref": "42", "ref_id": "114",
     "attrs": ":!NULL:",
     "field_attrs": {"required": true, "multi": false, "key": false, "alias": null, "default": ""}},
    {"num": 2, "id": "41", "val": "Email", "type": "3",
     "field_attrs": {"required": false, "multi": false, "key": false, "alias": null, "default": ""}},
    {"num": 4, "id": "156", "val": "Дата", "type": "9",
     "attrs": "{\"default\":\"[TODAY]\"}",
     "field_attrs": {"required": false, "multi": false, "key": false, "alias": null, "default": "[TODAY]"}}
  ]
}
  • id — id таблицы или колонки; val — имя; type — базовый тип;
  • unique — значения первой колонки уникальны;
  • ref — таблица-справочник у справочной колонки, ref_id — id ссылки на неё;
  • arr_id — подчинённая таблица;
  • field_attrs — атрибуты колонки объектом: required — обязательное поле, multi — множественный выбор, key — часть ключа уникальности, alias — псевдоним, default — значение по умолчанию; есть у каждой колонки;
  • attrs — те же атрибуты строкой, как они хранятся, только если заданы.

Атрибуты хранятся в одном из двух форматов: JSON ({"required":true,"default":"[TODAY]"}) или строкой, где :!NULL: — обязательное поле, :MULTI: — множественный выбор, :KEY: — ключ, :ALIAS=Имя: — псевдоним, а текст вне двоеточий — значение по умолчанию. field_attrs приводит оба формата к одному виду, поэтому читать удобнее его. Ключи совпадают с ключами поля в _d_schema. Прочие ключи атрибутов, например действие колонки-кнопки action, приходят в field_attrs как есть.

Тот же объект отдают edit_obj — в reqs.{id}.field_attrs — и object с JSON — в req_field_attrs.{id}.

Список таблиц

curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/dict?JSON=1"

Ответ

{"18": "Пользователь", "22": "Запрос", "42": "Роль", "269": "Настройка", "641": "Клиент"}

Схема одним запросом _d_schema

_d_schema принимает описание по именам и создаёт недостающее: таблицы, поля, ссылки, подчинённые таблицы, отчёты и роли с правами. Существующее находится по имени, поэтому повторный вызов того же описания ничего не дублирует. Описание передаётся полем json или телом Content-Type: application/json с _xsrf внутри.

curl -s "https://ideav.ru/mydb/_d_schema?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" --form-string 'json={
  "tables": [
    {"name": "Категория", "unique": true},
    {"name": "Клиент", "fields": [
      {"name": "ИНН"},
      {"name": "Категория", "ref": "Категория", "required": true},
      {"name": "Ответственный", "ref": "Пользователь"},
      {"name": "Дата", "type": "DATE", "default": "[TODAY]"}]},
    {"name": "Заказ", "fields": [
      {"name": "Клиент", "ref": "Клиент"},
      {"name": "Сумма", "type": "SIGNED"}],
     "children": [
      {"name": "Позиция", "fields": [{"name": "Кол-во", "type": "NUMBER"}]}]}],
  "reports": [
    {"name": "Заказы клиентов", "columns": [
      {"name": "Клиент", "table": "Клиент"},
      {"name": "Сумма", "table": "Заказ", "field": "Сумма", "func": "SUM"}]}],
  "roles": [
    {"name": "Менеджер", "description": "Ведёт клиентов и заказы", "grants": [
      {"table": "Клиент", "level": "WRITE"},
      {"table": "Клиент", "field": "Ответственный", "level": "WRITE", "mask": "[USER_ID]"},
      {"table": "Заказ", "level": "WRITE"},
      {"table": "Категория", "level": "READ"},
      {"report": "Заказы клиентов", "level": "READ"}]}]
}'

Ответ (сокращён): id всего описанного

{"ok": true,
 "tables": {"Клиент": {"id": 641, "created": true, "fields": {"ИНН": 643, "Категория": 646, "Ответственный": 651, "Дата": 653}},
            "Позиция": {"id": 700, "created": true, "fields": {"Кол-во": 702}, "parent": 690}, ...},
 "reports": {"Заказы клиентов": {"id": 950, "created": true, "columns": {"Клиент": 951, "Сумма": 952}}},
 "roles": {"Менеджер": {"id": 900, "created": true, "grants": [...]}},
 "created": 24, "updated": 0, "existing": 0}
КлючЧто задаёт
таблица: name, type, uniqueИмя таблицы (= первой колонки), её тип, справочник с уникальными значениями
таблица: fields[], children[]Поля и подчинённые таблицы (вложенность любая)
поле: typeSHORT (по умолчанию), CHARS, MEMO, NUMBER, SIGNED, DATE, DATETIME, BOOLEAN, FILE…; синонимы STRING, TEXT, INT, MONEY, BOOL
поле: ref, multiСсылка на таблицу по имени; ссылка на несколько записей
поле: required, key, defaultОбязательное, часть ключа уникальности, значение по умолчанию
право: table (+field), report, objectОбъект: таблица, её поле, отчёт или types / all / files
право: level, mask, export, deleteREAD / WRITE / BARRED, маска строк, флаги выгрузки и удаления
колонка отчётаname, table + field или formula, func, hide, sort, from / to
  • у существующего меняются только флаги: required, multi, key, default, unique, уровень и маска права, параметры колонки отчёта. Тип термина и цель ссылки не меняются;
  • имя термина одно на всю базу: поле «Сумма» двух таблиц — один термин. Тот же термин с другим типом — ошибка 400 с подсказкой;
  • ошибки описания проверяются до записи: ответ 400 {"ok": false, "errors": [...]}, база не меняется;
  • нужен доступ к редактору структуры, как для _d_*.

Создать таблицу

_d_new создаёт термин — таблицу из одной колонки. t — базовый тип первой колонки, val — имя, unique=1 — значения первой колонки уникальны (так делают справочники).

curl -s "https://ideav.ru/mydb/_d_new?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "t=3" -F "val=Клиент"

Ответ: id новой таблицы — в obj

{"id": "", "obj": 641, "next_act": "edit_types", "args": "ext", "warnings": ""}

Повторный вызов с тем же именем не создаёт дубль: вернётся id существующего термина и предупреждение в warnings.

Добавить колонку

Колонка добавляется в два шага: создать термин колонки (_d_new) и добавить его к таблице (_d_req). Если подходящий термин уже есть, первый шаг пропускают.

curl -s "https://ideav.ru/mydb/_d_new?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "t=3" -F "val=ИНН"
# → {"obj": 642, ...}
curl -s "https://ideav.ru/mydb/_d_req/641?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "t=642"
# → {"id": 643, "obj": 641, ...}

Id колонки — id из ответа _d_req (643), а не obj: дальше значение этой колонки пишется полем t643.

Колонка-справочник

Справочная колонка ссылается на другую таблицу. Шаги: таблица-справочник, ссылка на неё (_d_ref), ссылка как колонка (_d_req).

curl -s "https://ideav.ru/mydb/_d_new?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "t=3" -F "val=Категория" -F "unique=1"
# → {"obj": 644}
curl -s "https://ideav.ru/mydb/_d_ref/644?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF"
# → {"obj": 645}       — ссылка на Категорию
curl -s "https://ideav.ru/mydb/_d_req/641?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "t=645"
# → {"id": 646}        — колонка Категория у Клиента

Множественный выбор включает и выключает _d_multi на колонке:

curl -s "https://ideav.ru/mydb/_d_multi/646?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF"

Если в таблице несколько колонок со ссылкой на один справочник (Автор, Исполнитель, Контролёр — все из Пользователя), дайте им псевдонимы через _d_alias с id колонки:

curl -s "https://ideav.ru/mydb/_d_alias/648?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "val=Исполнитель"

Подчинённая таблица

Табличная часть (позиции заказа, этапы проекта) — это таблица, добавленная к другой таблице через _d_req без ссылки. Её записи создаются с родителем up={id записи}.

curl -s "https://ideav.ru/mydb/_d_new?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "t=3" -F "val=Позиция"
# → {"obj": 700}
curl -s "https://ideav.ru/mydb/_d_req/690?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "t=700"     # 690 — таблица Заказ

Обязательность и значение по умолчанию

_d_null переключает обязательность колонки. _d_attrs задаёт значение по умолчанию: константу или переменную: [TODAY] — сегодня, [USER_ID] — текущий пользователь. Результат виден в field_attrs колонки в metadata; задать всё сразу по имени колонки можно ключами required и default в _d_schema.

curl -s "https://ideav.ru/mydb/_d_null/643?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF"
curl -s "https://ideav.ru/mydb/_d_attrs/650?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "val=[TODAY]"

Переместить и удалить

_d_ord ставит колонку на позицию order (первую колонку двигать нельзя), _d_up — на одну позицию выше.

curl -s "https://ideav.ru/mydb/_d_ord/648?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "order=1"

_d_del_req удаляет колонку. Если в ней есть данные, сервер откажет; forced=1 удаляет вместе с данными. Сам термин колонки остаётся и может пригодиться в другой таблице.

curl -s "https://ideav.ru/mydb/_d_del_req/643?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "forced=1"

_d_del удаляет пустую таблицу или неиспользуемый термин. Порядок для таблицы с данными: записи, затем ссылки на неё и доступы, затем сама таблица.

curl -s "https://ideav.ru/mydb/_d_del/642?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF"

_d_save переименовывает таблицу или меняет её базовый тип: val — имя, t — базовый тип, unique=1 — признак уникальности.

Записи

Прочитать записи

Команда object с id таблицы отдаёт её записи. Формат JSON_OBJ — массив строк: i — id записи, u — родитель, o — порядок, r — значения колонок по порядку метаданных.

curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/object/641/?JSON_OBJ&LIMIT=100"

Ответ

[
  {"i": 649, "u": 1, "o": 1, "r": ["АО Ромашка", "7701234567", "660:Клиент"]},
  {"i": 668, "u": 1, "o": 2, "r": ["ПАО Эллипс", "7709876543", "650:Лид"]}
]

Справочное значение приходит как id:Название, множественное — id1,id2:Название1,Название2. С параметром NAMED строки приходят объектами с именами колонок вместо массива r.

ПараметрЧто делает
F_{id колонки}=значениеФильтр по колонке; вместо id можно имя колонки
F_{id таблицы}=значениеФильтр по первой колонке: её ключ — id самой таблицы
FR_{колонка}=…&TO_{колонка}=…Диапазон: от и до
F_U={id родителя}Записи подчинённой таблицы у одного родителя
F_I={id записи}Одна запись
ORDER={id колонки}Сортировка; ORDER=-{id} — по убыванию
LIMIT=100 · LIMIT=100,100Первые 100 записей · 100 записей, начиная со 101-й
_count=Только число записей: {"count": "42"}
full=1Не обрезать длинные значения

В значении фильтра работают условия: %ООО% — содержит, !значение — не равно, >100, <=20002, 10..20 — между, % — непусто, !% — пусто, IN(a,b) — одно из значений, @660 — ссылка на запись 660, @IN(21006,21014) — на одну из записей, [TODAY] и другие переменные.

# клиенты, в названии которых есть «Ромашка»: %25 — это % в адресе
curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/object/641/?JSON_OBJ&F_641=%25Ромашка%25"
# позиции заказа 691
curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/object/700/?JSON_OBJ&F_U=691"
# по именам таблицы и колонки
curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/object/Клиент?JSON_OBJ&F_ИНН=7701234567"

С NAMED поля приходят ключами-именами: ссылка — {"id", "val"}, подчинённая таблица — {"table", "count"}. Фильтры и права те же, что у JSON_OBJ.

curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/object/Клиент?JSON_OBJ&NAMED&LIMIT=1"

Ответ

[{"id": 649, "up": 1, "ord": 1,
  "Клиент": "АО Ромашка", "ИНН": "7701234567",
  "Категория": {"id": 660, "val": "Клиент"}}]

Подчинённую таблицу без F_U сервер отдаёт пустой: её записи висят на родителях, а не на корне.

Создать запись

_m_new с id таблицы. Главное значение записи пишется в t{id таблицы}, колонки — в t{id колонки}. up=1 — запись верхнего уровня, up={id записи} — подчинённая.

curl -s "https://ideav.ru/mydb/_m_new/641?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=1" -F "t641=АО Ромашка" -F "t643=7701234567" -F "t646=660"

Ответ

{"id": 649, "obj": 649, "ord": 1, "next_act": "object", "args": "", "val": "АО Ромашка"}

В справочную колонку пишется id записи справочника (t646=660). Позиция заказа 691:

curl -s "https://ideav.ru/mydb/_m_new/700?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "up=691" -F "t700=Труба 32 мм" -F "t702=12"

То же по именам: таблица — в адресе, поля — в fields, главное значение — под именем таблицы. Ссылку можно задать названием записи справочника: ядро найдёт её, если такая ровно одна.

curl -s "https://ideav.ru/mydb/_m_new/Клиент?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "up=1" \
  --form-string 'fields={"Клиент":"ПАО Эллипс","ИНН":"7709876543","Категория":"Лид"}'

Изменить запись

_m_set с id записи меняет переданные колонки, включая первую. Пустое значение очищает поле, у BOOLEAN снимают галку значением 0.

curl -s "https://ideav.ru/mydb/_m_set/649?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "t643=7701234568" -F "t646=661"

Признак удачной записи — "obj": 649 (id записи) и непустой id в ответе. В справочный ключ t{id} _m_set принимает только id; пробел (t646= ) очищает одиночную ссылку.

По именам — fields; запись можно назвать в адресе по имени таблицы и главному значению. В fields ссылка задаётся id или названием, множественная — массивом:

curl -s "https://ideav.ru/mydb/_m_set/Клиент/АО%20Ромашка?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" --form-string 'fields={"ИНН":"7701234568","Категория":"ЧС"}'

В колонку с множественным выбором передают id записей через запятую: t648=616,617.

Варианты для справочной колонки отдаёт _ref_reqs с id колонки; параметр id — запись, для которой выбирают значение:

curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/_ref_reqs/646?JSON=1&id=649"
# → {"650": "Лид", "660": "Клиент", "661": "ЧС"}

Удалить и переместить

_m_del удаляет запись вместе с подчинёнными; если на неё ссылаются другие записи, сервер ответит 409. _m_move переносит подчинённую запись к другому родителю, _m_up поднимает её на одну позицию среди соседей, _m_ord ставит на позицию order.

curl -s "https://ideav.ru/mydb/_m_del/668?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF"
curl -s "https://ideav.ru/mydb/_m_move/705?JSON=1&up=692" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF"

Несколько записей одной таблицы удаляет _m_del_batch (до 10 000 id), все записи под фильтром — object с _m_del_select:

curl -s "https://ideav.ru/mydb/_m_del_batch/641?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "ids=668,669,670"
# → {"deleted": [668, 669], "errors": {"670": "..."}}
curl -s "https://ideav.ru/mydb/object/641/?JSON=1&F_646=650" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "_m_del_select=1"
# → {"deleted": 31}

Пакет команд _m_batch

_m_batch выполняет до 1000 команд new, set, save, del одним запросом. Запись, созданная в том же пакете, адресуется как ":id" (последняя new) или ":idN" (операция new с номером N, считая с нуля).

curl -s "https://ideav.ru/mydb/_m_batch?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" --form-string 'ops=[
  {"op":"new","id":690,"up":1,"fields":{"t690":"Заказ 101"}},
  {"op":"new","id":700,"up":":id0","fields":{"t700":"Труба 32 мм","t702":"12"}},
  {"op":"new","id":700,"up":":id0","fields":{"t700":"Муфта","t702":"4"}},
  {"op":"set","id":649,"fields":{"t643":"7701234569"}}
]'

Ответ

{"results": [
  {"n": 0, "op": "new", "id": 812, "ok": true},
  {"n": 1, "op": "new", "id": 813, "ok": true},
  {"n": 2, "op": "new", "id": 814, "ok": true},
  {"n": 3, "op": "set", "id": 649, "ok": true}
], "ok": 4, "failed": 0}

Пакет не атомарен: упавшая операция не откатывает соседние. Разбирайте results и повторяйте только операции с "ok": false.

В пакете тоже можно по именам: таблица операции new — именем в id, поля — именами в fields. Имя, которого нет в таблице, не роняет операцию: оно попадёт в warnings, а поле не запишется.

curl -s "https://ideav.ru/mydb/_m_batch?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" --form-string 'ops=[
  {"op":"new","id":"Заказ","up":1,"fields":{"Заказ":"Заказ 102","Клиент":"АО Ромашка","Сумма":"15400"}},
  {"op":"new","id":"Позиция","up":":id0","fields":{"Позиция":"Труба 32 мм","Кол-во":"12"}},
  {"op":"set","id":":id0","fields":{"Сумма":"16200"}}
]'

Загрузка файлом

Тысячи записей загружают файлом: object/{таблица} с import=1 и файлом bki_file. Строка файла — значения колонок через ; в порядке метаданных, первой строкой — DATA. Справочные колонки задаются названиями, даты — ДД.ММ.ГГГГ, ; внутри значения экранируется как \;. Если первая колонка уникальна, существующие записи обновляются.

clients.bki

DATA
АО Ромашка;7701234567;Клиент;;
ПАО Эллипс;7709876543;Лид;;
curl -s "https://ideav.ru/mydb/object/641?JSON&import=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "bki_file=@clients.bki;type=text/plain"

Файл — до 8 МБ; большие объёмы делите на части по несколько тысяч строк. Для подчинённой таблицы добавьте в адрес F_U={id родителя}.

Пользователи, роли и доступ

Системные таблицы

Пользователи, роли, меню, доступы и запросы — обычные таблицы базы. Их записи создаются и меняются теми же _m_new, _m_set и _m_del.

ТаблицаidКолонки
Пользователь18t18 логин, t115 роль, t20 пароль, t33 имя, t41 email, t125 токен API
Роль42t42 название, t135 описание
Объекты (доступы роли)116t116 объект, t136 уровень, t49 маска; подчинена роли
Меню151t151 название, t153 адрес, t391 иконка; подчинено роли
Запрос22t22 название; колонки запроса — таблица 28
Функция63Справочник функций колонок запроса: SUM, COUNT, abn_ID…

Колонки Activity (t124), xsrf (t40) и счётчик неудачных входов (t301) заполняет сервер — не пишите в них.

Создать роль

curl -s "https://ideav.ru/mydb/_m_new/42?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=1" -F "t42=Менеджер" -F "t135=Ведёт клиентов и заказы"
# → {"id": 900, ...}

Роли базы — object/42/?JSON_OBJ. Роль admin с полным доступом — запись 145. Id роли по названию:

curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/object/Роль?JSON_OBJ&F_Роль=Менеджер"
# → [{"i": 900, "u": 1, "o": 1, "r": ["Менеджер", "", "", "Ведёт клиентов и заказы"]}]

Создать пользователя и задать пароль

Пользователь — запись таблицы 18: логин в t18, роль в t115, пароль в t20. Пароль сохраняется хешем, прочитать его обратно нельзя.

curl -s "https://ideav.ru/mydb/_m_new/18?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=1" -F "t18=ivanov" -F "t115=900" -F "t20=Str0ng-Pass" -F "t33=Иван Иванов" -F "t41=ivanov@example.com"
# → {"id": 910, ...}

Сменить пароль или роль — _m_set с id пользователя. Смена пароля закрывает все его сессии.

curl -s "https://ideav.ru/mydb/_m_set/910?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "t20=N3w-Pass"
curl -s "https://ideav.ru/mydb/_m_set/910?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "t115=901"

Пользователь для интеграции получает вместо пароля токен API в t125 — с ним он работает заголовком X-Authorization (см. «Токен API»):

curl -s "https://ideav.ru/mydb/_m_new/18?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=1" -F "t18=api" -F "t115=900" -F "t125=$(openssl rand -hex 32)"

Выдать доступ

Доступ роли к таблице, колонке или запросу — запись таблицы 116, подчинённая роли: up — роль, t116 — объект, t136 — уровень.

t136Уровень
53READ — чтение
54WRITE — чтение и изменение
52BARRED — нет доступа (колонка не приходит даже в API)
curl -s "https://ideav.ru/mydb/_m_new/116?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=900" -F "t116=641" -F "t136=54"   # Клиент: запись
curl -s "https://ideav.ru/mydb/_m_new/116?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=900" -F "t116=644" -F "t136=53"   # Категория: чтение
curl -s "https://ideav.ru/mydb/_m_new/116?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=900" -F "t116=643" -F "t136=52"   # колонка ИНН скрыта

Уровень таблицы наследуют её колонки; отдельной колонке его можно понизить. Кроме таблиц в t116 бывают особые объекты: 0 — редактор структуры (WRITE на него нужен для команд _d_*), 1 — все таблицы, 10 — файловый менеджер. Поля t55 и t56 разрешают роли выгрузку и удаление.

Новая таблица не выдана ни одной роли: без доступа её записи отвечают 403, а выпадающий список справочника пуст. Выдавайте доступ сразу после _d_new всем ролям, чьи экраны ею пользуются.

Доступы роли — object/116/?JSON_OBJ&F_U={роль}, роли с доступом к таблице — object/42/?JSON_OBJ&F_116={таблица}.

Роль со всеми правами по именам таблиц, полей и отчётов задаётся одним вызовом _d_schema — ключ roles (см. «Схема одним запросом»):

curl -s "https://ideav.ru/mydb/_d_schema?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" --form-string 'json={"roles": [{"name": "Менеджер", "grants": [
    {"table": "Клиент", "level": "WRITE"},
    {"table": "Клиент", "field": "ИНН", "level": "BARRED"},
    {"table": "Категория", "level": "READ"}]}]}'

Маски: только свои записи

Маска t49 у доступа фильтрует строки, которые увидит роль. Её значение работает как фильтр F_ по колонке из t116.

# менеджер видит только клиентов, где он — ответственный (колонка 651 ссылается на Пользователя)
curl -s "https://ideav.ru/mydb/_m_new/116?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=900" -F "t116=651" -F "t136=54" -F "t49=[USER_ID]"
# пользователь видит в таблице Пользователь только себя
curl -s "https://ideav.ru/mydb/_m_new/116?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=900" -F "t116=18" -F "t136=53" -F "t49=[USER]"
  • переменные: [USER] — логин, [USER_ID] — id пользователя, [ROLE], [ROLE_ID];
  • значение или условие: 164, %ООО%, @IN(21006,21014);
  • [Имя запроса] — подставляется первая ячейка первой строки запроса, например список id через GROUP_CONCAT;
  • маски на одну колонку складываются по ИЛИ, на разные колонки — по И.

Гостевой доступ

Посетитель без входа работает пользователем guest, если в базе есть и роль guest, и пользователь guest с этой ролью. Что видит гость, решают доступы и меню роли guest.

Запросы (отчёты)

Как устроен запрос

Запрос — запись таблицы 22, его колонки — записи таблицы 28 с up={id запроса}. У колонки есть источник (таблица или колонка), имя, формула, фильтр и функция. Как соединить таблицы и по чему группировать, ядро определяет само по метаданным.

ПолеЧто задаёт
t28Источник: id таблицы (её главное значение) или id колонки; 0 — вычисляемая колонка
t100Имя в отчёте: ключ в JSON_KV и имя фильтра FR_{имя}. Без имени колонку снаружи не отфильтровать
t101Формула: у колонки с источником — псевдоним (одно слово), у вычисляемой — SQL-выражение над псевдонимами
t102 / t103Фильтр: значение от / до
t104Функция: SUM, COUNT, AVG, MIN, MAX, GROUP_CONCAT; abn_ID — id записи
t105 / t106Фильтр по значению функции (HAVING)
t1071 — скрыть колонку: она фильтрует и связывает, но не выводится
t109Сортировка: 1, 2…; минус — по убыванию
t84Формат: NUMBER, SIGNED, DATE, HTML
t72Итог по колонке в форме отчёта: SUM, COUNT

Первая колонка задаёт главную таблицу: строки отчёта — её записи. Колонки связанных таблиц добавляются просто id их колонок — путь по ссылкам ядро находит само.

Создать запрос

Отчёт «Клиенты по категориям»: категория, число клиентов и сумма заказов.

curl -s "https://ideav.ru/mydb/_m_new/22?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" -F "up=1" -F "t22=Клиенты по категориям"
# → {"id": 950}
curl -s "https://ideav.ru/mydb/_m_new/28?JSON=1&up=950" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=950" -F "t28=646" -F "t100=Категория" -F "t109=1"
curl -s "https://ideav.ru/mydb/_m_new/28?JSON=1&up=950" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=950" -F "t28=641" -F "t100=Клиентов" -F "t104=COUNT" -F "t72=SUM"
curl -s "https://ideav.ru/mydb/_m_new/28?JSON=1&up=950" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=950" -F "t28=695" -F "t100=Сумма" -F "t104=SUM" -F "t84=NUMBER"

GROUP BY строится по всем видимым колонкам без функции — здесь по Категории. В _m_new функцию можно указать именем (t104=SUM), в _m_set — только id записи из таблицы Функция: object/63/?JSON_OBJ.

Колонки запроса — object/28/?JSON_OBJ&F_U=950, список запросов — object/22/?JSON_OBJ.

Формулы и вычисляемые колонки

Колонке-источнику дают псевдоним в t101, вычисляемая колонка (t28=0) использует его в SQL-выражении. В формуле — псевдоним, а не имя в отчёте.

curl -s "https://ideav.ru/mydb/_m_new/28?JSON=1&up=960" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=960" -F "t28=720" -F "t100=Мест" -F "t101=мест" -F "t107=1"
curl -s "https://ideav.ru/mydb/_m_new/28?JSON=1&up=960" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=960" -F "t28=721" -F "t100=Куплено" -F "t101=куплено"
curl -s "https://ideav.ru/mydb/_m_new/28?JSON=1&up=960" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=960" -F "t28=0" -F "t100=Загрузка, %" -F "t101=ROUND(куплено/мест*100, 2)"
  • псевдоним — одно слово, не совпадающее с SQL-словами (id, val, SUM);
  • агрегат внутри формулы: ROUND(SUM(Оценка), 1);
  • [THIS] — собственное значение колонки: COALESCE([THIS], 0);
  • ссылка: CONCAT('<a href="', Адрес, '">открыть</a>') с t84=HTML;
  • SELECT, FROM, TABLE в формулах запрещены — выборка из другой таблицы делается подзапросом.

Фильтры

Постоянный фильтр задаётся в t102 (от) и t103 (до) у колонки. Колонку только для фильтра скрывают (t107=1). Если у колонки нет «Имени в отчёте» (t100), её фильтр снаружи не переопределить — так задают ограничители, см. «Права на запрос».

t102Отбирает
ОдобренаРавно значению; у справочной колонки — по названию
660 при t104=abn_IDСсылка на запись справочника 660
% / !%Непусто / пусто
!1Не равно 1
>=[WEEK_AGO], [TODAY], [YESTERDAY], [MONTH_AGO], [NOW]Относительные даты
1.1 и t103=31.12Диапазон дат текущего года
[USER_ID] при t104=abn_IDСтроки текущего пользователя
_request_.ProjIDПараметр из адреса: report/950?ProjID=12
[Имя запроса]Значение другого запроса
# только свои заказы: колонка Ответственный (697) скрыта и фильтрует по текущему пользователю
curl -s "https://ideav.ru/mydb/_m_new/28?JSON=1&up=950" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=950" -F "t28=697" -F "t104=abn_ID" -F "t102=[USER_ID]" -F "t107=1"

Подзапросы

Суммы с разных уровней цепочки (Компания ← Заявка ← Договор) в одном запросе удваиваются: строки соединённых таблиц перемножаются. Каждую следующую сумму считают отдельным запросом и вставляют в формулу как [Имя запроса].

# подзапрос «Договоры компании»: сумма договоров компании [КомпанияID]
curl -s "https://ideav.ru/mydb/_m_new/22?JSON=1" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=1" -F "t22=Договоры компании"
# → {"id": 970}
curl -s "https://ideav.ru/mydb/_m_new/28?JSON=1&up=970" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=970" -F "t28=812" -F "t100=Сумма" -F "t104=SUM"
curl -s "https://ideav.ru/mydb/_m_new/28?JSON=1&up=970" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=970" -F "t28=803" -F "t104=abn_ID" -F "t102=[КомпанияID]" -F "t107=1"
# основной запрос: псевдоним КомпанияID и вычисляемая колонка с подзапросом
curl -s "https://ideav.ru/mydb/_m_new/28?JSON=1&up=980" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=980" -F "t28=800" -F "t100=КомпанияID" -F "t101=КомпанияID" -F "t104=abn_ID" -F "t107=1"
curl -s "https://ideav.ru/mydb/_m_new/28?JSON=1&up=980" \
  -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF" \
  -F "up=980" -F "t28=0" -F "t100=Законтрактовано" -F "t101=IFNULL([Договоры компании],0)"

Подзапрос без строк возвращает пусто — оберните его в IFNULL или задайте запросу «Если пусто, вернуть» (t113=0).

Выполнить запрос

report с id или названием запроса. JSON_KV — массив объектов с ключами из t100; JSON — с описанием колонок и форматов.

curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/report/950?JSON_KV"

Ответ

[
  {"Категория": "Клиент", "Клиентов": "12", "Сумма": "1840000"},
  {"Категория": "Лид", "Клиентов": "31", "Сумма": "0"}
]

Фильтры снаружи — FR_{имя} (от) и TO_{имя} (до), пробелы в имени заменяются на _. Они работают на колонках с заполненным «Именем в отчёте» (t100) и перекрывают их t102/t103; колонки без имени параметрами не меняются. ORDER={id колонки} сортирует (минус — по убыванию), LIMIT ограничивает число строк, RECORD_COUNT возвращает только их число.

curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/report/950?JSON_KV&FR_Категория=Клиент"
curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/report/951?JSON_KV&FR_Дата=01.10.2026&TO_Дата=31.10.2026&LIMIT=50"
# запрос по названию
curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/report/Клиенты%20по%20категориям?JSON_KV&FR_Категория=Клиент"

Права на запрос

Запрос работает как хранимая процедура: с правами того, кто его настроил, а не того, кто вызывает. Роли нужен только доступ READ к самому запросу (t116={id запроса}, t136=53). Он открывает все колонки и строки, которые отбирает запрос; доступы роли к таблицам и её маски к запросу не применяются.

Это не обход прав, а назначение запроса: отдать ровно то, что решил аналитик, в том числе из таблиц, которые роль руками не видит. Что увидит вызывающий, ограничивают фильтры внутри запроса, и внешний параметр их не отменит:

  • снаружи фильтруются только колонки с заполненным «Именем в отчёте» (t100): FR_{имя} и TO_{имя} перекрывают их t102/t103;
  • у колонки без t100 фильтр t102/t103 параметрами адреса не переопределяется — это защита от подмены фильтра пользователем;
  • фильтруемость включается явно, именем колонки: всё, чему аналитик не дал имени, закрыто для подмены. Это строже правила «фильтровать можно всё, кроме запрещённого».
# ограничитель «только свои заказы»: без t100, скрыт — FR_/TO_ его не заменят
curl -s "https://ideav.ru/mydb/_m_new/28?JSON=1&up=950"   -H "X-Authorization: $TOKEN" -F "_xsrf=$XSRF"   -F "up=950" -F "t28=697" -F "t104=abn_ID" -F "t102=[USER_ID]" -F "t107=1"

Не давайте колонке-ограничителю «Имя в отчёте». С именем её фильтр становится параметром: вызывающий подставит в FR_ чужой id вместо [USER_ID] и получит чужие строки.

Файлы и рабочие места

Файловый менеджер

dir_admin загружает HTML-шаблоны рабочих мест (templates=1) и скрипты со стилями (download=1). Кроме заголовка нужны кука idb_{база}, поле token и _xsrf, полученный под этой кукой. Без куки сервер отвечает InvalidToken.

XSRF=$(curl -s -b "idb_mydb=$TOKEN" "https://ideav.ru/mydb/xsrf?JSON=1" | jq -r ._xsrf)
curl -s -H "X-Authorization: $TOKEN" -b "idb_mydb=$TOKEN" "https://ideav.ru/mydb/dir_admin/?JSON=1" \
  -F "_xsrf=$XSRF" -F "token=$TOKEN" -F "templates=1" -F "add_path=/" -F "upload=Загрузить" -F "rewrite=1" \
  -F "userfile=@clients.html"

Файлы из download=1 раздаются по адресу https://ideav.ru/download/mydb/. Адрес рабочего места в меню роли — имя шаблона без .html (t153=clients). Доступ роли к файлам — особый объект 10 в таблице 116.

Резервная копия

backup создаёт архив базы в папке backups файлового менеджера. Делайте его перед массовыми изменениями структуры. Выгрузка одной таблицы — object/{таблица} с параметром csv, bki или json_export.

curl -s -b "idb_mydb=$TOKEN" "https://ideav.ru/mydb/backup"
curl -s -H "X-Authorization: $TOKEN" "https://ideav.ru/mydb/object/641/?JSON=1&bki" -o clients.bki

Приложение с нуля: порядок шагов

Как excel-to-app собирает приложение

Сборщик excel-to-app делает из таблицы Excel готовое приложение только перечисленными командами. Порядок такой:

  • База — my/_new_db с шаблоном RU.
  • Пользователь для API — _m_new/18 с токеном в t125 и ролью admin (t115=145).
  • Справочники, таблицы, ссылки, роли с правами и отчёты — одним описанием _d_schema; то, чего описание не покрывает, — пошагово: _d_new, _d_req, _d_ref.
  • Данные — _m_batch или загрузка файлом object/{таблица}?import=1.
  • Роли — _m_new/42, доступы к каждой таблице и справочнику — _m_new/116, маски «только свои» — t49.
  • Пользователи ролей с паролями — _m_new/18 с t115 и t20.
  • Запросы для отчётов и дашбордов — _m_new/22 и колонки _m_new/28; доступ READ к ним ролям.
  • Рабочие места — шаблоны через dir_admin, пункты меню ролей — _m_new/151.
  • Проверка — вход каждым пользователем через auth и чтение его экранов и отчётов под его токеном.

Проверять права нужно под токеном каждой роли: у администратора базы доступ полный, и отказов он не увидит.