Перейти к содержанию

Согласование изменений

Владелец рабочего пространства может потребовать, чтобы административные изменения подтверждали участники, прежде чем они применятся. Это отдельный механизм от аппрувов транзакций: правило задаётся на действие, а не политикой, и подтверждают его владелец, администраторы или выбранная группа.

По умолчанию согласование выключено. Владелец включает его для каждого действия отдельно.

Какие действия

Действие Что покрывает operation в запросе
Управление пользователями приглашение пользователя, создание API-пользователя, удаление пользователя и API-пользователя, смена роли, блокировка и разблокировка пользователя create, api_user_create, delete, role_change, block, unblock
Управление группами создание, переименование и удаление группы, состав участников create, update, delete, add_members, remove_members
Управление согласованием порог администраторов и правила согласования, включая их выключение threshold, action_setting
Добавление адреса в белый список новый адрес во внешнем кошельке нет
Управление тегами создание, изменение и удаление тега, привязка и отвязка тегов к аккаунтам create, update, delete, attach_detach
Переводы на one-time адреса разрешение и запрет переводов по адресу вне белого списка нет
Управление политиками изменение правил политики и откат к версии из истории update, rollback

Правило одно на действие: нельзя требовать согласования удаления, но не смены роли. Изменения администратора платформы (PLATFORM_ADMIN) согласования не требуют; у поддержки платформы (PLATFORM_SUPPORT) такого обхода нет.

Кто подтверждает

Режим Значение mode Кто подтверждает
Владелец QUORUM_APPROVAL_MODE_OWNER_ONLY владелец рабочего пространства
Кворум администраторов QUORUM_APPROVAL_MODE_ADMIN_QUORUM администраторы, включая неподписывающих; порог общий для всех действий в этом режиме
Группа QUORUM_APPROVAL_MODE_SPECIFIC_GROUP участники выбранной группы, чьей роли доступно мобильное приложение (все роли, кроме наблюдателя и оператора), со своим порогом

К кворуму администраторов или группе можно добавить обязательное подтверждение владельца.

Режим «Группа» требует включённого согласования действия «Управление группами»; само это действие в режиме «Группа» включается.

Изменение правил Ответ
включить правило в режиме «Группа» или перевести на группу включённое, пока согласование действия «Управление группами» выключено QUORUM_GROUP_MANAGEMENT_APPROVAL_DISABLED
выключить согласование действия «Управление группами», пока включено правило в режиме «Группа» QUORUM_GROUP_MANAGEMENT_APPROVAL_IN_USE; в details.action_keys — ключи таких правил

Подтверждение принимается только с привязанного устройства в мобильном приложении — это второй фактор. Инициатор тоже подтверждает свой запрос, владелец в режиме «Владелец» подтверждает сам.

Заблокировать рабочее пространство нельзя

Правила согласования подтверждает только владелец. Правило, состав которого сейчас не набирает порог, включить нельзя: ответ QUORUM_NO_ELIGIBLE_APPROVERS. Порог администраторов не поднимается выше их числа, пока включено правило в режиме кворума.

Настройки согласования меняет владелец. Администраторы, включая неподписывающих, могут предлагать изменения, пока владелец держит включённым согласование действия «Управление согласованием». Выключение этого согласования отменяет неподтверждённые запросы на изменение настроек.

«Управление согласованием» Изменение владельца Изменение администратора
выключено (по умолчанию) применяется сразу не принимается: QUORUM_CHANGE_REQUESTS_DISABLED
включено запрос на согласование, владелец подтверждает сам запрос на согласование, подтверждает владелец

Как проходит согласование

.........Pert......... ............ИнициаторPertУчастники согласования(мобильное приложение)ИнициаторPertУчастники согласования(мобильное приложение)действие, например удаление пользователя403 QUORUM_APPROVAL_REQUIRED+ идентификатор запросаpush-уведомление,запрос в очереди согласованияподтвердить или отклонить(с привязанного устройства)alt[подтверждений достаточно]изменение применяетсяEXECUTED, либо FAILED с причиной[отказ, после которого порог не набрать]REJECTED[срок вышел]EXPIRED[запрос отменили]отменаCANCELLED
.........Pert......... ............ИнициаторPertУчастники согласования(мобильное приложение)ИнициаторPertУчастники согласования(мобильное приложение)действие, например удаление пользователя403 QUORUM_APPROVAL_REQUIRED+ идентификатор запросаpush-уведомление,запрос в очереди согласованияподтвердить или отклонить(с привязанного устройства)alt[подтверждений достаточно]изменение применяетсяEXECUTED, либо FAILED с причиной[отказ, после которого порог не набрать]REJECTED[срок вышел]EXPIRED[запрос отменили]отменаCANCELLED
  1. Инициатор выполняет действие как обычно. Если для него включено согласование, изменение не применяется, а ответ приходит с кодом QUORUM_APPROVAL_REQUIRED и идентификатором запроса.
  2. Тем, кто подтверждает, приходит push-уведомление на привязанное устройство. Они подтверждают или отклоняют запрос в мобильном приложении.
  3. Когда подтверждений достаточно, изменение проверяется заново и применяется само. Повторять действие не нужно.
  4. Отказ, после которого порог уже не набрать, закрывает запрос. Пока запрос ждёт голосов, его можно отменить. Запрос без нужных голосов истекает через 72 часа.

Состав тех, кто подтверждает, фиксируется при создании запроса и может только сокращаться. Кто потерял право подтверждать, исключается из запроса при ближайшем голосе или повторной отправке того же изменения: голосовать по нему он больше не может, даже вернув право, а его голос не засчитывается. До исключения запрос ещё виден ему в списке ждущих голоса.

Право подтверждать теряют, когда:

  • сменилась роль или человека убрали из группы подтверждающих;
  • отвязано последнее устройство, например при смене телефона: роль, которой нужно устройство, неактивна до новой привязки;
  • пользователь заблокирован в рабочем пространстве или на платформе;
  • аккаунт ждёт настройки двухфакторной аутентификации.

Запрос устаревает, если после создания у правила согласования изменились условие одобрения или порог либо после исключения выбывших порог уже не набрать. Устаревший запрос отменяется при первом голосе или повторной попытке того же изменения, и повторная попытка создаёт новый запрос. Пока запрос не устарел, повторная попытка возвращает его же.

Статус Значение
PENDING ждёт подтверждений
APPROVED подтверждений достаточно, изменение применяется
EXECUTED изменение применено. Если применять к этому моменту уже нечего, правка тега, привязка уже привязанного тега, настройка one-time адресов, блокировка уже заблокированного и разблокировка уже разблокированного пользователя тоже закрываются EXECUTED — без изменений
FAILED подтверждено, но применить нельзя; причина — в карточке запроса. Например: инициатор к этому моменту заблокирован, удалён из рабочего пространства или потерял права на это изменение, пользователь уже в этой роли или уже в группе, адрес уже в белом списке, рабочее пространство заморожено, тег уже отвязан, тег изменился после запроса, политика изменилась после запроса
REJECTED отклонено
EXPIRED 72 часа прошли без нужных подтверждений
CANCELLED отменено до решения: инициатором, владельцем или администратором, выключением согласования «Управления согласованием» (запросы на изменение настроек) или потому, что запрос устарел

Ход согласования приходит событиями по WebSocket участникам согласования: инициатору, тем, кто подтверждает, и тем, кто видит все запросы рабочего пространства. На привязанное устройство события приходят только инициатору и тем, кто подтверждает. Каждое решение попадает в журнал аудита.

Просмотр изменений в мобильном приложении

Небольшое изменение — например, новый адрес в белом списке или правка нескольких полей одного правила политики без перемещения — показано прямо в карточке запроса в мобильном приложении: что было и что станет.

Если изменение большое, карточка показывает краткую сводку и кнопку «Посмотреть изменения». Кнопка открывает экран изменений с полным описанием «было → стало», добавленными и удалёнными значениями и пояснениями.

Подтверждают и отклоняют запрос в карточке: с экрана изменений вернитесь назад. У большого изменения кнопка «Подтвердить» доступна только после того, как экран изменений показал описание; отклонить можно сразу.

Состояние Что показано
Описание изменения ещё не загрузилось сводка и кнопка «Посмотреть изменения»; «Подтвердить» — как у большого изменения
Описание изменения требует новой версии мобильного приложения сводка, плашка «Обновите приложение, чтобы увидеть изменение целиком» и основные данные изменения; кнопки просмотра нет, «Подтвердить» доступна сразу
В изменении есть ослабление контроля (например, снятие запрета) или предупреждение плашка в карточке: у большого изменения «Есть важные изменения: откройте их перед решением», у небольшого — «Есть важные изменения: проверьте их перед решением» над ним
Описания изменения нет основные данные изменения в карточке, без «было → стало»
Описание изменения урезано плашка «Показано не всё: описание урезано по размеру» на экране изменений
При открытии экрана изменений запрос уже не ждёт подтверждений или не найден плашка «Запрос больше не ожидает решения»; описание, если оно есть, показано

Одно изменение объекта за раз

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

Объект Что считается его изменением
политика транзакций обновление правил и откат к версии из истории
правило согласования действия изменение правила
порог кворума администраторов изменение порога
пользователь смена роли, блокировка, разблокировка, удаление
группа изменение, удаление, добавление и удаление участников
тег изменение и удаление; привязка и отвязка тегов объект не держат
one-time адреса рабочего пространства разрешение и запрет
Изменение объекта, пока по нему есть запрос в работе Ответ
другое изменение — и требующее согласования, и не требующее его сейчас CHANGE_REQUEST_IN_PROGRESS
то же самое изменение, пока согласование включено и запрос ждёт подтверждений как при регистрации: QUORUM_APPROVAL_REQUIRED с этим запросом
изменение администратора платформы (PLATFORM_ADMIN) принимается, запрос остаётся в работе

Чтобы изменить объект по-другому, запрос в работе нужно закрыть: дождаться решения или срока либо отменить его.

Отмена

Кто Что может отменить
инициатор свой запрос
владелец любой запрос рабочего пространства, который ждёт подтверждений
администраторы, включая неподписывающих запрос действия, в правиле которого включено admins_can_cancel

admins_can_cancel по умолчанию выключено; у правила «Управление согласованием» его включить нельзя. Администратор может отменить и запрос, который касается его самого. Подтверждения с устройства отмена не требует.

Для интеграции

Ответ на действие, которому нужно согласование:

{
  "error": "change requires quorum approval",
  "code": "QUORUM_APPROVAL_REQUIRED",
  "details": {
    "approval_request_uuid": "3f0c5c1e-…",
    "action": "<action_key>",
    "operation": "delete",
    "mode": "QUORUM_APPROVAL_MODE_OWNER_ONLY",
    "expires_at": "2026-09-17T10:00:00Z"
  }
}

Ответ на изменение объекта, по которому есть запрос в работе:

{
  "error": "another change request for this object is in progress",
  "code": "CHANGE_REQUEST_IN_PROGRESS",
  "details": {
    "approval_request_uuid": "3f0c5c1e-…",
    "action": "<action_key>",
    "operation": "update",
    "status": "PENDING",
    "initiator_uuid": "7d1e…",
    "initiator": { "user_uuid": "7d1e…", "user_type": "user", "email": "anna@example.com", "first_name": "Anna" },
    "initiated_by_me": false,
    "can_cancel": true,
    "created_at": "2026-10-05T10:00:00Z",
    "expires_at": "2026-10-08T10:00:00Z"
  }
}
Поле details Когда есть
expires_at у запроса в PENDING
initiator_uuid, initiator только если вызывающему доступна карточка запроса — он участник запроса или у него VIEW_CHANGE_APPROVALS; initiator может отсутствовать и у них
can_cancel может отсутствовать

Ключ действия отдаёт GET …/quorum/actions полем key, в ответе на действие он приходит полем action. Режим везде передаётся значением из таблицы режимов: в ответе на действие, в запросах согласования и в настройках.

Ответ действия Когда
403 QUORUM_APPROVAL_REQUIRED правило включено; запрос зарегистрирован, его данные — в details
409 QUORUM_NO_ELIGIBLE_APPROVERS правило включено, но подтверждать сейчас некому; изменение не сохраняется
409 CHANGE_REQUEST_IN_PROGRESS по объекту изменения уже есть запрос в работе, см. Одно изменение объекта за раз; изменение не сохраняется, в details — этот запрос
503 CHANGE_APPROVAL_UNAVAILABLE согласование временно недоступно; изменение не сохраняется, повторите позже
Метод Путь Право
GET /api/v2/admin/tenants/{tenant_uuid}/change-approvals?scope=PENDING_MY_VOTE\|INITIATED_BY_ME\|ALL пользователь рабочего пространства; ALL — VIEW_CHANGE_APPROVALS; с привязанного устройства — только PENDING_MY_VOTE и INITIATED_BY_ME пользователя, привязавшего устройство к этому рабочему пространству
GET /api/v2/admin/change-approvals?scope=… — тот же список сразу по всем рабочим пространствам, доступным токену; страница общая как у списка рабочего пространства, по каждому пространству отдельно; ALL — только пространства с VIEW_CHANGE_APPROVALS, ни одного — 403
GET …/change-approvals/{request_uuid} участник запроса или VIEW_CHANGE_APPROVALS; с устройства — участник
POST …/change-approvals/{request_uuid}/approve, …/reject участник согласования, с привязанного устройства
POST …/change-approvals/{request_uuid}/cancel инициатор запроса, владелец и, если правило действия это разрешает (admins_can_cancel), администраторы, см. Отмена; с привязанного устройства — если роль требует привязки устройства
GET /api/v2/admin/tenants/{tenant_uuid}/quorum VIEW_QUORUM_SETTINGS
GET …/quorum/actions — список действий: ключ key (он же {action_key} в пути PUT) и fixed_mode у действия, режим которого не выбирается VIEW_QUORUM_SETTINGS
PUT …/quorum/actions/{action_key}, …/quorum/threshold MANAGE_QUORUM_SETTINGS; PROPOSE_QUORUM_SETTINGS — только запросом, пока включено «Управление согласованием»
GET …/quorum/gated-actions — действия рабочего пространства и requires_approval: потребует ли действие у вызывающего согласования; предупреждение до отправки изменения, прав на настройки не требует пользователь или API-пользователь с активной ролью в рабочем пространстве; с устройства — 403

Устройству, привязанному в нескольких рабочих пространствах, хватает одного запроса: у каждого элемента списка своё пространство в tenant_uuid и tenant_name, а initiated_by_me считается по пользователю, привязавшему устройство к этому пространству. Карточка и голос — по путям пространства элемента.

Голос подписывается ключом привязанного устройства, который открывается биометрией или ключом безопасности. В теле approve и reject приложение передаёт signed_vote — JSON голоса в base64 — и signature, подпись Ed25519 этих байтов, тоже в base64:

{"type": "change_vote", "tenant_uuid": "…", "request_uuid": "3f0c5c1e-…",
 "vote": "approve", "device_uuid": "…", "changes_hash": "9f2c…"}

changes_hash — отпечаток описания из карточки, по которой голосуют, пусто без описания. Голос approve засчитывается, только если он совпадает с changes_hash запроса; reject — при любом.

GET /api/v2/admin/change-approvals?scope=PENDING_MY_VOTE&limit=50
{
  "items": [
    { "request_uuid": "3f0c5c1e-…", "tenant_uuid": "0f8b7a5e-…", "tenant_name": "Treasury",
      "action": "<action_key>", "action_display_name": "Manage users", "status": "PENDING", "initiated_by_me": false },
    { "request_uuid": "a91d07b2-…", "tenant_uuid": "7c2e41d9-…", "tenant_name": "Trading",
      "action": "<action_key>", "action_display_name": "Manage tags", "status": "PENDING", "initiated_by_me": true }
  ],
  "total": 2,
  "limit": 50,
  "offset": 0
}

Ответ на принятый голос и на отмену — обновлённая карточка запроса. Отмена переводит в CANCELLED запрос, открытые sets[] и неподанные голоса, поданные голоса остаются; кто и почему отменил — в cancelled_by и cancel_reason.

POST /api/v2/admin/tenants/{tenant_uuid}/change-approvals/3f0c5c1e-…/cancel
{
  "request": {
    "request_uuid": "3f0c5c1e-…",
    "status": "CANCELLED",
    "cancel_reason": "owner",
    "cancelled_by": { "user_uuid": "5b2f…", "user_type": "user", "email": "boss@example.com" },
    "sets": [
      { "external_id": "OWNER", "threshold": 1, "status": "CANCELLED",
        "approvers": [ { "user_uuid": "5b2f…", "status": "CANCELLED", "is_me": false } ] }
    ]
  }
}
Ответ На что Когда
CHANGE_VOTE_DEVICE_REQUIRED approve, reject голос не с привязанного устройства
CHANGE_VOTE_SIGNATURE_REQUIRED approve, reject голос не подписан ключом устройства: версия приложения устарела, его нужно обновить
CHANGE_VOTE_SIGNATURE_INVALID approve, reject голос подписан не ключом этого устройства или подписан другой голос; у approve — и подписанный changes_hash не совпадает с запросом
CHANGE_VOTE_NOT_ALLOWED approve, reject голосующий не входит в состав подтверждающих запроса или исключён из него, потеряв право подтверждать
CHANGE_VOTE_CONFLICT approve, reject противоположный голос уже подан; повтор того же голоса ничего не меняет
CHANGE_REQUEST_CANCEL_NOT_ALLOWED cancel нет права отменить запрос, см. Отмена
200, та же карточка cancel запрос уже отменён
CHANGE_REQUEST_ALREADY_FINALIZED все три запрос уже закрыт или истёк; на голос — и когда запрос устарел: у правила согласования изменились условие одобрения или порог либо состав изменился так, что порог уже не набрать. Запрос при этом отменяется
CHANGE_REQUEST_NOT_FOUND все три запроса нет в рабочем пространстве; то же у карточки

Карточка запроса несёт всё, что нужно показать человеку, без дополнительных запросов:

{
  "request": {
    "request_uuid": "3f0c5c1e-…",
    "tenant_uuid": "…",
    "tenant_name": "Treasury",
    "action": "<action_key>",
    "action_display_name": "Manage users",
    "initiator": { "user_uuid": "7d1e…", "user_type": "user", "email": "owner@example.com", "first_name": "Anna", "last_name": "Smirnova", "api_user_name": "" },
    "initiated_by_me": false,
    "can_cancel": true,
    "cancel_reason": "",
    "payload": { "operation": "delete", "tenant_uuid": "…", "user_uuid": "9a4c…" },
    "display": { "user": { "user_uuid": "9a4c…", "user_type": "user", "email": "ivan@example.com", "first_name": "Ivan" }, "current_role": "EDITOR" },
    "status": "PENDING",
    "summary": {},
    "changes_hash": "",
    "entities": {},
    "sets": [
      {
        "external_id": "OWNER",
        "threshold": 1,
        "status": "PENDING",
        "approvers": [ { "user_uuid": "5b2f…", "status": "PENDING", "user": { "user_uuid": "5b2f…", "user_type": "user", "email": "boss@example.com", "first_name": "", "last_name": "", "api_user_name": "" }, "is_me": true } ]
      }
    ]
  }
}
Поле Что содержит
initiator, sets[].approvers[].user участник: user_type — user или api_user; у пользователя email, first_name, last_name, у API-пользователя api_user_name
initiated_by_me, sets[].approvers[].is_me этот участник — вы; с привязанного устройства — пользователь, привязавший его к этому рабочему пространству
can_cancel вы можете отменить запрос сейчас, см. Отмена; есть и в списке
cancel_reason почему запрос в CANCELLED: initiator, owner, admin — отменил человек; rule_changed, approvers_changed — запрос устарел; change_requests_disabled — выключено «Управление согласованием». Пусто у запросов, отменённых раньше, чем появилось поле
cancelled_by кто отменил, участник, как initiator; поля нет, если запрос устарел
payload что будет изменено
display названия и имена того, на что ссылается payload, на момент создания запроса; поля нет, если снимка нет
summary однострочная сводка изменения по локалям шаблона, en есть всегда; есть и в списке; пусто, если описания изменения нет
changes описание изменения «было → стало» на момент создания запроса; в карточке и ответах на голос и отмену; поля нет, если описания нет
changes_hash отпечаток changes: меняется вместе с ним; клиент его не вычисляет, а только сравнивает; есть и в списке, пусто без описания
entities пользователи, сервисные аккаунты и группы из changes — свежие на момент ответа: ключ "<kind>:<id>" (kind — user, service_account, user_group), значение — name, subtitle (email), subtype (user или api_user), is_me, missing; там же, где changes, иначе пустой объект
Действие operation Поля display
Управление пользователями role_change, delete, block, unblock user, current_role
Управление группами update, delete group_name
add_members, remove_members group_name, users — по порядку payload; у длинного списка только первые участники
Управление согласованием action_setting action_display_name, group_name
Добавление адреса в белый список нет whitelisted_wallet_name, currency_symbol, currency_descriptor, blockchain_name
Управление тегами update, delete tag
attach_detach vaults, tags
Управление политиками update policy_type_name
rollback policy_type_name, version_published_at

Остальные операции приходят без display: их payload читается как есть.

Состав подтверждающих в ответе настроек:

Поле Что содержит
approver_sets, reachable у правила кто подтверждал бы сейчас и набирает ли состав порог; есть и у выключенных правил
owner_approvers, admin_quorum_approvers кто прямо сейчас подтверждает как владелец и как администратор, независимо от правил
active_member_uuids у группы в списке групп участники, которые считаются в режиме «Группа»

Состав ещё не сохранённого правила считается из этих пулов, участников группы и порога.

Описание изменения

changes описывает изменение так, что его можно показать, не зная схемы payload действия: секции (например, правила политики) с полями «было» и «стало». Его присылают управление политиками (update, rollback), управление тегами (все операции), добавление адреса в белый список и управление one-time адресами. Запрос без описания (остальные действия, а у перечисленных — если описание не собралось) приходит без поля changes, с пустыми summary и changes_hash: показывайте его по display.

Пример — фрагмент карточки с описанием изменения правила политики: выросла сумма и изменился набор аппруверов A (остальные поля карточки, поля со значениями по умолчанию и часть шаблонов texts опущены):

{
  "request": {
    "request_uuid": "3f0c5c1e-…",
    "changes": {
      "schema_version": 1,
      "summary": { "code": "policy.summary.update.transfer", "args": { "rules": "1" } },
      "sections": [
        {
          "id": "5b0d2f1c-…", "kind": "policy.rule", "op": "CHANGE_VIEW_OP_MODIFIED",
          "title": { "code": "policy.rule.title", "args": { "name": "Large transfers" } },
          "position_before": 3, "position_after": 1,
          "fields": [
            { "id": "amount", "label": { "code": "policy.field.amount" }, "op": "CHANGE_VIEW_OP_MODIFIED",
              "before": { "money": { "amount": "1000", "currency": "USD" } },
              "after": { "money": { "amount": "5000", "currency": "USD" } } },
            { "id": "approval.sets.A.members", "op": "CHANGE_VIEW_OP_MODIFIED",
              "label": { "code": "policy.field.approval.set.members", "args": { "set": "A" } },
              "after": { "set": { "items": [
                { "op": "CHANGE_VIEW_OP_ADDED", "value": { "entity": { "kind": "user_group", "id": "33f1…" } } }
              ] } } },
            { "id": "approval.sets.A.threshold", "op": "CHANGE_VIEW_OP_MODIFIED", "important": true,
              "label": { "code": "policy.field.approval.set.threshold", "args": { "set": "A" } },
              "before": { "number": "2" }, "after": { "number": "1" } }
          ]
        }
      ],
      "notes": [ { "severity": "CHANGE_VIEW_NOTE_SEVERITY_INFO", "text": { "code": "policy.note.order.transfer" } } ],
      "texts": {
        "policy.summary.update.transfer": { "by_locale": { "en": "Transfers policy: {rules} rule(s) affected", "ru": "Политика переводов: затронуто правил — {rules}" } },
        "policy.field.amount": { "by_locale": { "en": "Amount from", "ru": "Сумма от" } }
      }
    },
    "summary": { "en": "Transfers policy: 1 rule(s) affected", "ru": "Политика переводов: затронуто правил — 1" },
    "changes_hash": "9f2c…",
    "entities": { "user_group:33f1…": { "name": "Finance" } }
  }
}
Поле changes Что содержит
summary однострочная сводка — текст
fields поля верхнего уровня — у простых изменений (порог 2 → 3)
sections секции — у составных изменений, в порядке показа; у секции id, kind (подсказка для особого показа, например policy.rule), title, op, позиции, fields, вложенные sections, notes и hidden_unchanged — сколько неизменённых полей и секций не перечислено
notes пояснения и предупреждения: severity — CHANGE_VIEW_NOTE_SEVERITY_INFO или CHANGE_VIEW_NOTE_SEVERITY_WARNING; пустое значение и CHANGE_VIEW_NOTE_SEVERITY_UNSPECIFIED показываются как CHANGE_VIEW_NOTE_SEVERITY_INFO, незнакомое — как CHANGE_VIEW_NOTE_SEVERITY_WARNING; text
truncated описание урезано по размеру и показывает не всё
texts шаблоны текстов: код → by_locale → шаблон
schema_version сейчас 1; у описания старшей версии показывается сводка, а само изменение — по display, как у запроса без описания

Текст приходит кодом и аргументами (code, args). Шаблон кода берётся из словаря клиента, иначе из texts на локали клиента, иначе на en, иначе показывается сам код.

Подстановка — имя аргумента в фигурных скобках: строчные латинские буквы, цифры и _. Она делается за один проход: {set} заменяется на args.set, {…} внутри значения аргумента не раскрывается, подстановка без аргумента и прочие скобки остаются как есть.

Значение аргумента — готовая строка, часто текст пользователя, например название правила. Показывайте каждое значение изолированно от соседнего текста, как элемент bdi в HTML: символы направления текста в нём не должны переставлять остальную строку.

op Секция Поле
CHANGE_VIEW_OP_ADDED добавлена; position_before — 0 значение в after
CHANGE_VIEW_OP_REMOVED удалена; position_after — 0 значение в before
CHANGE_VIEW_OP_MODIFIED изменена; позиции могут различаться before и after; у множества — after с элементами, отмеченными CHANGE_VIEW_OP_ADDED и CHANGE_VIEW_OP_REMOVED
CHANGE_VIEW_OP_MOVED изменилась только позиция —
CHANGE_VIEW_OP_UNCHANGED без изменений, показана для контекста значение в after

important у поля — изменение ослабляет контроль: снятие запрета, снижение порога.

Вид значения Что содержит
text текст кодом и аргументами
literal строка как есть: название, адрес
flag, number да или нет; десятичное число
money amount в валюте currency (USD) или, без currency, в единицах актива asset; при minor_units — в минимальных единицах актива
entity сущность kind и id: имя — из entities карточки, если ключ там есть и не missing, иначе name снимка, а без него — id; subtitle — уточнение; missing — сущности нет; виды — в таблице ниже
enum_value label для показа, code — значение
set элементы items с value и op: в изменённом множестве — CHANGE_VIEW_OP_ADDED или CHANGE_VIEW_OP_REMOVED, элемент как есть — CHANGE_VIEW_OP_UNSPECIFIED или CHANGE_VIEW_OP_UNCHANGED; omitted — сколько элементов не перечислено
list упорядоченный список items; omitted
time, unset время; значение не задано
незнакомый вид показывается строка fallback
kind сущности Что это Имя
user, service_account, user_group пользователь, сервисный аккаунт, группа свежее, в entities карточки
vault аккаунт снимок в name на момент создания запроса
tag тег снимок в name
whitelisted_wallet кошелёк белого списка снимок в name; subtitle — тип: internal, external, contract
asset актив снимок: name — символ, subtitle — сеть