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

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

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

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

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

Действие Что покрывает 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

Согласование добавления адреса в белый список, тегов, переводов на one-time адреса и политик включается на стороне платформы: до включения правило для этих действий сохраняется, а изменение применяется сразу.

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

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

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

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

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

Изменение правил и групп Ответ
включить правило в режиме «Группа» или перевести на группу включённое, пока согласование действия «Управление группами» выключено QUORUM_GROUP_MANAGEMENT_APPROVAL_DISABLED
выключить согласование действия «Управление группами», пока включено правило в режиме «Группа» QUORUM_GROUP_MANAGEMENT_APPROVAL_IN_USE; в details.action_keys — ключи таких правил
удалить группу, выбранную в правиле (даже выключенном) GROUP_REFERENCED_BY_QUORUM_SETTINGS; сначала переведите правило в другой режим
PUT правила без mode, с group_uuid не в режиме «Группа» или без него в режиме «Группа», с group_threshold больше 1000 (без поля или 0 — порог 1) 400 VALIDATION_ERROR
PUT правила действия с fixed_mode («Управление согласованием») в другом режиме, с группой или с admins_can_cancel 400 VALIDATION_ERROR
PUT порога администраторов вне 1..1000 400 VALIDATION_ERROR
группа из group_uuid не найдена GROUP_NOT_FOUND
группа из другого рабочего пространства QUORUM_GROUP_NOT_IN_TENANT

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

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

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

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

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

Правило, состав которого сейчас не набирает порог, включить нельзя: ответ 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. Отказ, после которого порог уже не набрать, закрывает запрос. Пока запрос ждёт голосов, его можно отменить. Запрос без нужных голосов истекает (EXPIRED).

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

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

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

Пока запрос не устарел и ждёт голосов, то же изменение от любого пользователя рабочего пространства возвращает этот же запрос (тот же approval_request_uuid). Инициатором остаётся создавший его: второй отправитель может отменить запрос только как владелец или администратор (см. Отмена).

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

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

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

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

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

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

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

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

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

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

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

Отмена

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

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

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

Ответ на действие, которому нужно согласование (текст error у каждого действия свой, опознавайте ответ по code):

{
  "error": "…",
  "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 Когда есть
approval_request_uuid, action, status, initiated_by_me, created_at всегда
operation у действий с operation в таблице «Какие действия»
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 — этот запрос
409 CHANGE_REQUEST_ALREADY_FINALIZED такой же запрос только что закрылся; изменение не сохраняется, повторите действие. У управления политиками код в теле — INTERNAL_ERROR при том же статусе
503 CHANGE_APPROVAL_UNAVAILABLE проверить, нужно ли согласование, сейчас нельзя; изменение не сохраняется, повторите позже. У белого списка, тегов и one-time адресов тот же сбой может прийти как 500 INTERNAL_ERROR
Метод Путь Право
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 подтверждающий этого запроса (входит в sets[]), с привязанного устройства
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 — режим, заданный платформой; QUORUM_APPROVAL_MODE_UNSPECIFIED — режим выбирает владелец VIEW_QUORUM_SETTINGS
PUT …/quorum/actions/{action_key}, …/quorum/threshold MANAGE_QUORUM_SETTINGS; PROPOSE_QUORUM_SETTINGS — только запросом, пока включено «Управление согласованием»
GET …/quorum/gated-actions — действия рабочего пространства и requires_approval: потребует ли действие у вызывающего согласования; права на настройки не нужны пользователь или API-пользователь с активной ролью в рабочем пространстве; с устройства — 403

Параметры списков: scope (по умолчанию PENDING_MY_VOTE), statuses (можно несколько: PENDING, APPROVED, REJECTED, EXPIRED, EXECUTED, FAILED, CANCELLED), action, limit (по умолчанию 50, не больше 200), offset; сортировка — новые первыми.

Устройству, привязанному в нескольких рабочих пространствах, хватает одного запроса: у каждого элемента списка своё пространство в 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",
    "execution_error": "",
    "expires_at": "2026-10-08T10:00:00Z",
    "created_at": "2026-10-05T10:00:00Z",
    "updated_at": "2026-10-05T10:00:00Z",
    "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; поля нет, если запрос устарел или отменён раньше, чем появилось поле
execution_error причина отказа при FAILED; у остальных статусов — пустая строка
expires_at, created_at, updated_at срок, время создания и последнего изменения запроса, RFC 3339
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 читается как есть.

Правило действия задаёт PUT …/quorum/actions/{action_key}; ответ — сохранённое правило с составом подтверждающих. group_threshold без поля — 1; admins_can_cancel без поля сохраняет прежнее значение.

PUT /api/v2/admin/tenants/{tenant_uuid}/quorum/actions/{action_key}
{
  "is_enabled": true,
  "mode": "QUORUM_APPROVAL_MODE_SPECIFIC_GROUP",
  "group_uuid": "33f1…",
  "group_threshold": 2,
  "owner_approval": false,
  "admins_can_cancel": false
}
{
  "setting": {
    "action_key": "<action_key>",
    "display_name": "Manage users",
    "is_enabled": true,
    "mode": "QUORUM_APPROVAL_MODE_SPECIFIC_GROUP",
    "group_uuid": "33f1…",
    "group_threshold": 2,
    "owner_approval": false,
    "admins_can_cancel": false,
    "approver_sets": [ { "external_id": "A", "threshold": 2, "user_uuids": [ "5b2f…", "7d1e…", "9a4c…" ] } ],
    "reachable": true
  }
}

Порог кворума администраторов задаёт PUT …/quorum/threshold с телом {"admin_quorum_threshold": 2} (1..1000); ответ — настройки целиком, как у GET …/quorum.

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

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

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

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

changes описывает изменение так, что его можно показать, не зная схемы payload действия: секции (например, правила политики) с полями «было» и «стало».

Действие changes
управление политиками (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 внешний (whitelisted) кошелёк снимок в name; subtitle — тип: internal, external, contract
asset актив снимок: name — символ, subtitle — сеть