@v3finance/core-sdk 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/CHANGELOG.md +441 -7
  2. package/README.md +219 -73
  3. package/dist/cjs/client.d.ts +67 -22
  4. package/dist/cjs/client.d.ts.map +1 -1
  5. package/dist/cjs/client.js +38 -2
  6. package/dist/cjs/client.js.map +1 -1
  7. package/dist/cjs/core/errors.d.ts +30 -10
  8. package/dist/cjs/core/errors.d.ts.map +1 -1
  9. package/dist/cjs/core/errors.js +47 -15
  10. package/dist/cjs/core/errors.js.map +1 -1
  11. package/dist/cjs/core/gated.d.ts +30 -6
  12. package/dist/cjs/core/gated.d.ts.map +1 -1
  13. package/dist/cjs/core/gated.js +21 -6
  14. package/dist/cjs/core/gated.js.map +1 -1
  15. package/dist/cjs/core/http.d.ts +12 -8
  16. package/dist/cjs/core/http.d.ts.map +1 -1
  17. package/dist/cjs/core/http.js +147 -43
  18. package/dist/cjs/core/http.js.map +1 -1
  19. package/dist/cjs/core/pagination.d.ts +28 -1
  20. package/dist/cjs/core/pagination.d.ts.map +1 -1
  21. package/dist/cjs/core/pagination.js +36 -1
  22. package/dist/cjs/core/pagination.js.map +1 -1
  23. package/dist/cjs/generated/operations.js +1 -1
  24. package/dist/cjs/generated/types.d.ts +965 -179
  25. package/dist/cjs/generated/types.d.ts.map +1 -1
  26. package/dist/cjs/index.d.ts +32 -8
  27. package/dist/cjs/index.d.ts.map +1 -1
  28. package/dist/cjs/index.js +12 -0
  29. package/dist/cjs/index.js.map +1 -1
  30. package/dist/cjs/resources/addresses.d.ts +23 -8
  31. package/dist/cjs/resources/addresses.d.ts.map +1 -1
  32. package/dist/cjs/resources/addresses.js +8 -1
  33. package/dist/cjs/resources/addresses.js.map +1 -1
  34. package/dist/cjs/resources/admin.d.ts +150 -25
  35. package/dist/cjs/resources/admin.d.ts.map +1 -1
  36. package/dist/cjs/resources/admin.js +94 -23
  37. package/dist/cjs/resources/admin.js.map +1 -1
  38. package/dist/cjs/resources/approvals.d.ts +46 -5
  39. package/dist/cjs/resources/approvals.d.ts.map +1 -1
  40. package/dist/cjs/resources/approvals.js +27 -4
  41. package/dist/cjs/resources/approvals.js.map +1 -1
  42. package/dist/cjs/resources/balances.d.ts +152 -16
  43. package/dist/cjs/resources/balances.d.ts.map +1 -1
  44. package/dist/cjs/resources/balances.js +9 -5
  45. package/dist/cjs/resources/balances.js.map +1 -1
  46. package/dist/cjs/resources/categories.d.ts +93 -21
  47. package/dist/cjs/resources/categories.d.ts.map +1 -1
  48. package/dist/cjs/resources/categories.js +65 -18
  49. package/dist/cjs/resources/categories.js.map +1 -1
  50. package/dist/cjs/resources/counterparties.d.ts +26 -3
  51. package/dist/cjs/resources/counterparties.d.ts.map +1 -1
  52. package/dist/cjs/resources/counterparties.js +17 -0
  53. package/dist/cjs/resources/counterparties.js.map +1 -1
  54. package/dist/cjs/resources/folders.d.ts +51 -10
  55. package/dist/cjs/resources/folders.d.ts.map +1 -1
  56. package/dist/cjs/resources/folders.js +36 -5
  57. package/dist/cjs/resources/folders.js.map +1 -1
  58. package/dist/cjs/resources/history.d.ts +20 -2
  59. package/dist/cjs/resources/history.d.ts.map +1 -1
  60. package/dist/cjs/resources/history.js.map +1 -1
  61. package/dist/cjs/resources/me.d.ts +27 -4
  62. package/dist/cjs/resources/me.d.ts.map +1 -1
  63. package/dist/cjs/resources/me.js +22 -1
  64. package/dist/cjs/resources/me.js.map +1 -1
  65. package/dist/cjs/resources/members.d.ts +19 -4
  66. package/dist/cjs/resources/members.d.ts.map +1 -1
  67. package/dist/cjs/resources/members.js +15 -6
  68. package/dist/cjs/resources/members.js.map +1 -1
  69. package/dist/cjs/resources/operations.d.ts +64 -8
  70. package/dist/cjs/resources/operations.d.ts.map +1 -1
  71. package/dist/cjs/resources/operations.js +26 -2
  72. package/dist/cjs/resources/operations.js.map +1 -1
  73. package/dist/cjs/resources/registries.d.ts +87 -9
  74. package/dist/cjs/resources/registries.d.ts.map +1 -1
  75. package/dist/cjs/resources/registries.js +54 -11
  76. package/dist/cjs/resources/registries.js.map +1 -1
  77. package/dist/cjs/resources/reports.d.ts +300 -41
  78. package/dist/cjs/resources/reports.d.ts.map +1 -1
  79. package/dist/cjs/resources/reports.js +135 -7
  80. package/dist/cjs/resources/reports.js.map +1 -1
  81. package/dist/cjs/resources/sharing.d.ts +61 -10
  82. package/dist/cjs/resources/sharing.d.ts.map +1 -1
  83. package/dist/cjs/resources/sharing.js +51 -4
  84. package/dist/cjs/resources/sharing.js.map +1 -1
  85. package/dist/cjs/resources/transfers.d.ts +44 -6
  86. package/dist/cjs/resources/transfers.d.ts.map +1 -1
  87. package/dist/cjs/resources/transfers.js +41 -3
  88. package/dist/cjs/resources/transfers.js.map +1 -1
  89. package/dist/cjs/resources/types.d.ts +16 -0
  90. package/dist/cjs/resources/types.d.ts.map +1 -1
  91. package/dist/cjs/units.d.ts.map +1 -1
  92. package/dist/cjs/units.js +18 -2
  93. package/dist/cjs/units.js.map +1 -1
  94. package/dist/cjs/version.d.ts +1 -1
  95. package/dist/cjs/version.js +1 -1
  96. package/dist/esm/client.d.ts +67 -22
  97. package/dist/esm/client.d.ts.map +1 -1
  98. package/dist/esm/client.js +38 -2
  99. package/dist/esm/client.js.map +1 -1
  100. package/dist/esm/core/errors.d.ts +30 -10
  101. package/dist/esm/core/errors.d.ts.map +1 -1
  102. package/dist/esm/core/errors.js +47 -15
  103. package/dist/esm/core/errors.js.map +1 -1
  104. package/dist/esm/core/gated.d.ts +30 -6
  105. package/dist/esm/core/gated.d.ts.map +1 -1
  106. package/dist/esm/core/gated.js +21 -6
  107. package/dist/esm/core/gated.js.map +1 -1
  108. package/dist/esm/core/http.d.ts +12 -8
  109. package/dist/esm/core/http.d.ts.map +1 -1
  110. package/dist/esm/core/http.js +148 -44
  111. package/dist/esm/core/http.js.map +1 -1
  112. package/dist/esm/core/pagination.d.ts +28 -1
  113. package/dist/esm/core/pagination.d.ts.map +1 -1
  114. package/dist/esm/core/pagination.js +36 -1
  115. package/dist/esm/core/pagination.js.map +1 -1
  116. package/dist/esm/generated/operations.js +1 -1
  117. package/dist/esm/generated/types.d.ts +965 -179
  118. package/dist/esm/generated/types.d.ts.map +1 -1
  119. package/dist/esm/index.d.ts +32 -8
  120. package/dist/esm/index.d.ts.map +1 -1
  121. package/dist/esm/index.js +17 -5
  122. package/dist/esm/index.js.map +1 -1
  123. package/dist/esm/resources/addresses.d.ts +23 -8
  124. package/dist/esm/resources/addresses.d.ts.map +1 -1
  125. package/dist/esm/resources/addresses.js +8 -1
  126. package/dist/esm/resources/addresses.js.map +1 -1
  127. package/dist/esm/resources/admin.d.ts +150 -25
  128. package/dist/esm/resources/admin.d.ts.map +1 -1
  129. package/dist/esm/resources/admin.js +94 -23
  130. package/dist/esm/resources/admin.js.map +1 -1
  131. package/dist/esm/resources/approvals.d.ts +46 -5
  132. package/dist/esm/resources/approvals.d.ts.map +1 -1
  133. package/dist/esm/resources/approvals.js +27 -4
  134. package/dist/esm/resources/approvals.js.map +1 -1
  135. package/dist/esm/resources/balances.d.ts +152 -16
  136. package/dist/esm/resources/balances.d.ts.map +1 -1
  137. package/dist/esm/resources/balances.js +9 -5
  138. package/dist/esm/resources/balances.js.map +1 -1
  139. package/dist/esm/resources/categories.d.ts +93 -21
  140. package/dist/esm/resources/categories.d.ts.map +1 -1
  141. package/dist/esm/resources/categories.js +65 -18
  142. package/dist/esm/resources/categories.js.map +1 -1
  143. package/dist/esm/resources/counterparties.d.ts +26 -3
  144. package/dist/esm/resources/counterparties.d.ts.map +1 -1
  145. package/dist/esm/resources/counterparties.js +17 -0
  146. package/dist/esm/resources/counterparties.js.map +1 -1
  147. package/dist/esm/resources/folders.d.ts +51 -10
  148. package/dist/esm/resources/folders.d.ts.map +1 -1
  149. package/dist/esm/resources/folders.js +36 -5
  150. package/dist/esm/resources/folders.js.map +1 -1
  151. package/dist/esm/resources/history.d.ts +20 -2
  152. package/dist/esm/resources/history.d.ts.map +1 -1
  153. package/dist/esm/resources/history.js.map +1 -1
  154. package/dist/esm/resources/me.d.ts +27 -4
  155. package/dist/esm/resources/me.d.ts.map +1 -1
  156. package/dist/esm/resources/me.js +22 -1
  157. package/dist/esm/resources/me.js.map +1 -1
  158. package/dist/esm/resources/members.d.ts +19 -4
  159. package/dist/esm/resources/members.d.ts.map +1 -1
  160. package/dist/esm/resources/members.js +15 -6
  161. package/dist/esm/resources/members.js.map +1 -1
  162. package/dist/esm/resources/operations.d.ts +64 -8
  163. package/dist/esm/resources/operations.d.ts.map +1 -1
  164. package/dist/esm/resources/operations.js +26 -2
  165. package/dist/esm/resources/operations.js.map +1 -1
  166. package/dist/esm/resources/registries.d.ts +87 -9
  167. package/dist/esm/resources/registries.d.ts.map +1 -1
  168. package/dist/esm/resources/registries.js +54 -11
  169. package/dist/esm/resources/registries.js.map +1 -1
  170. package/dist/esm/resources/reports.d.ts +300 -41
  171. package/dist/esm/resources/reports.d.ts.map +1 -1
  172. package/dist/esm/resources/reports.js +135 -7
  173. package/dist/esm/resources/reports.js.map +1 -1
  174. package/dist/esm/resources/sharing.d.ts +61 -10
  175. package/dist/esm/resources/sharing.d.ts.map +1 -1
  176. package/dist/esm/resources/sharing.js +51 -4
  177. package/dist/esm/resources/sharing.js.map +1 -1
  178. package/dist/esm/resources/transfers.d.ts +44 -6
  179. package/dist/esm/resources/transfers.d.ts.map +1 -1
  180. package/dist/esm/resources/transfers.js +41 -3
  181. package/dist/esm/resources/transfers.js.map +1 -1
  182. package/dist/esm/resources/types.d.ts +16 -0
  183. package/dist/esm/resources/types.d.ts.map +1 -1
  184. package/dist/esm/units.d.ts.map +1 -1
  185. package/dist/esm/units.js +18 -2
  186. package/dist/esm/units.js.map +1 -1
  187. package/dist/esm/version.d.ts +1 -1
  188. package/dist/esm/version.js +1 -1
  189. package/docs/concepts.md +193 -0
  190. package/docs/errors.md +491 -0
  191. package/package.json +7 -6
  192. package/src/client.ts +89 -25
  193. package/src/core/errors.ts +49 -15
  194. package/src/core/gated.ts +31 -7
  195. package/src/core/http.ts +173 -49
  196. package/src/core/pagination.ts +38 -1
  197. package/src/generated/operations.ts +1 -1
  198. package/src/generated/types.ts +1001 -180
  199. package/src/index.ts +64 -7
  200. package/src/resources/addresses.ts +24 -8
  201. package/src/resources/admin.ts +186 -33
  202. package/src/resources/approvals.ts +55 -9
  203. package/src/resources/balances.ts +186 -14
  204. package/src/resources/categories.ts +125 -30
  205. package/src/resources/counterparties.ts +23 -6
  206. package/src/resources/folders.ts +74 -18
  207. package/src/resources/history.ts +27 -2
  208. package/src/resources/me.ts +30 -5
  209. package/src/resources/members.ts +26 -7
  210. package/src/resources/operations.ts +73 -8
  211. package/src/resources/registries.ts +90 -11
  212. package/src/resources/reports.ts +348 -67
  213. package/src/resources/sharing.ts +71 -18
  214. package/src/resources/transfers.ts +51 -7
  215. package/src/resources/types.ts +16 -0
  216. package/src/units.ts +19 -2
  217. package/src/version.ts +1 -1
@@ -1,11 +1,11 @@
1
- /** Единая форма ошибки для ВСЕХ эндпоинтов. Приводится слоем middleware/errorEnvelope независимо от того, как ошибку собрал хендлер */
1
+ /** Единая форма ошибки. Приводится слоем middleware/errorEnvelope независимо от того, как ошибку собрал хендлер. Слой узнаёт ошибку по ключу `error` в корне тела; ответ со статусом ≥400 без этого ключа он не трогает — так отвечает пакетная выдача доступа, когда отклонены все получатели: 400 с телом `{results}` */
2
2
  export interface ApiErrorEnvelope {
3
3
  error: {
4
4
  /** Машиночитаемый код. Осмысленный там, где его задаёт хендлер (INSUFFICIENT_BALANCE, POLICY_DENIED, APPROVAL_UNSATISFIABLE…), иначе выведен из HTTP-статуса (NOT_FOUND, FORBIDDEN…) */
5
5
  code: string;
6
6
  /** Человекочитаемое описание — можно показывать пользователю */
7
7
  message: string;
8
- /** Структурированные подробности, если есть */
8
+ /** Структурированные подробности, если есть (поля валидации, трейс политик). Сюда же слой переносит машиночитаемые ключи, которые хендлер положил рядом с error: `field` — ключ поля категории, не прошедшего валидацию; `stepIndex` — номер отказавшего шага композитной заявки */
9
9
  details?: unknown;
10
10
  };
11
11
  }
@@ -142,6 +142,8 @@ export interface AddressAccessGrant {
142
142
  canCreateAddress: boolean;
143
143
  grantorId: string;
144
144
  status: "active" | "revoked";
145
+ revokedAt: string | null;
146
+ revokedById: string | null;
145
147
  /** format: date-time */
146
148
  createdAt: string;
147
149
  /** format: date-time */
@@ -227,8 +229,8 @@ export interface AddressDetail {
227
229
  tokenId: string | null;
228
230
  /** format: date-time */
229
231
  createdAt: string;
230
- /** Сводка совместного доступа. Приходит в списке и детали адреса; PATCH /addresses/{id} переиспользует эту же схему и sharing НЕ возвращает */
231
- sharing?: SharingSummary;
232
+ /** Сводка совместного доступа. Приходит всегда: в списке, ответе PATCH и детали адреса */
233
+ sharing: SharingSummary;
232
234
  /** Код токена адреса (из segmentsJson + asset route) */
233
235
  asset: string | null;
234
236
  assetName: string | null;
@@ -340,8 +342,17 @@ export interface AddressGroupDetail {
340
342
  createdAt: string;
341
343
  /** format: date-time */
342
344
  updatedAt: string;
343
- /** range: -9007199254740991..9007199254740991 */
345
+ /**
346
+ * Сколько адресов в папке. Приходит в списке и детали
347
+ *
348
+ * range: -9007199254740991..9007199254740991
349
+ */
344
350
  memberCount?: number;
351
+ /** Служебный счётчик ORM — приезжает в списке и детали рядом с memberCount и равен ему. Читайте memberCount: это поле — деталь реализации, объявлено только потому, что фактически отдаётся */
352
+ _count?: {
353
+ /** range: -9007199254740991..9007199254740991 */
354
+ members: number;
355
+ };
345
356
  /** Как у меня доступ к группе */
346
357
  access?: "manager" | "granted" | "admin";
347
358
  /** Открыта ли папка кому-то ещё + сколько ЛЮДЕЙ имеют доступ. Ортогонально полю access: своя расшаренная папка — это access=manager + isShared=true */
@@ -376,8 +387,17 @@ export interface AddressGroup {
376
387
  createdAt: string;
377
388
  /** format: date-time */
378
389
  updatedAt: string;
379
- /** range: -9007199254740991..9007199254740991 */
390
+ /**
391
+ * Сколько адресов в папке. Приходит в списке и детали
392
+ *
393
+ * range: -9007199254740991..9007199254740991
394
+ */
380
395
  memberCount?: number;
396
+ /** Служебный счётчик ORM — приезжает в списке и детали рядом с memberCount и равен ему. Читайте memberCount: это поле — деталь реализации, объявлено только потому, что фактически отдаётся */
397
+ _count?: {
398
+ /** range: -9007199254740991..9007199254740991 */
399
+ members: number;
400
+ };
381
401
  /** Как у меня доступ к группе */
382
402
  access?: "manager" | "granted" | "admin";
383
403
  /** Открыта ли папка кому-то ещё + сколько ЛЮДЕЙ имеют доступ. Ортогонально полю access: своя расшаренная папка — это access=manager + isShared=true */
@@ -441,8 +461,8 @@ export interface AddressListItem {
441
461
  assetSymbol: string | null;
442
462
  /** Баланс в decimal (по token.decimals). NULL — ledger-счёта ещё нет */
443
463
  balanceDecimal: string | null;
444
- /** Сводка совместного доступа. Приходит в списке и детали адреса; PATCH /addresses/{id} переиспользует эту же схему и sharing НЕ возвращает */
445
- sharing?: SharingSummary;
464
+ /** Сводка совместного доступа. Приходит всегда: в списке, ответе PATCH и детали адреса */
465
+ sharing: SharingSummary;
446
466
  /** Владелец адреса (создатель) с UI-метаданными */
447
467
  owner: MemberCard | null;
448
468
  access: AddressAccess;
@@ -725,11 +745,11 @@ export interface ApprovalRequestDetail {
725
745
  }
726
746
  export interface ApprovalRequestEvent {
727
747
  id: string;
728
- /** created|voted|approved|rejected|cancelled|applied|apply_failed|stale */
748
+ /** created|voted|approved|rejected|cancelled|applied|apply_failed|stale|folder_skipped. folder_skipped идёт ПОСЛЕ applied: адрес по заявке create_address создан, но в папку, выбранную при подаче, не положен (папку удалили или у инициатора отозвали право) — причина в comment */
729
749
  type: string;
730
750
  actorId: string | null;
731
751
  decision: string | null;
732
- /** Пояснение ПОДПИСАНТА. У события applied всегда null — id созданного объекта лежит в resultId, чтобы клиент не напечатал его как комментарий */
752
+ /** У voted — пояснение ПОДПИСАНТА; у stale, apply_failed и folder_skipped — системное пояснение для человека. У события applied всегда null — id созданного объекта лежит в resultId, чтобы клиент не напечатал его как комментарий */
733
753
  comment: string | null;
734
754
  /** Только у applied: id созданного/изменённого объекта */
735
755
  resultId: string | null;
@@ -878,7 +898,7 @@ export interface AssignCategoryBody {
878
898
  * Значения полей; обязательные (required) должны быть заполнены
879
899
  * @defaultValue {}
880
900
  */
881
- values: {
901
+ values?: {
882
902
  [key: string]: unknown;
883
903
  };
884
904
  /** length: 0..500 */
@@ -939,7 +959,12 @@ export interface CancelResult {
939
959
  }
940
960
  /** Тело не требуется — инициатор из сессии */
941
961
  export type CancelTransferBody = Record<string, never>;
942
- export type CategoryAccessScope = unknown;
962
+ /**
963
+ * Что категория открывает по умолчанию, без гранта (какое именно право — зависит от поля: viewScope, assignScope, editScope). owner_only — ничего сверх доступа через адрес операции (свои операции, совместный доступ) и грантов категории; vault_members — право есть у каждого участника Vault'а.
964
+ *
965
+ * В ответах приходят только эти два значения. В ЗАПРОСЕ дополнительно принимается устаревший псевдоним "restricted": записывается как "owner_only" (вёл себя так же; миграция 0037 убрала его из данных). В перечень значений он не входит намеренно: описание общее для запроса и ответа, а в ответе его не бывает. Новому коду слать его незачем
966
+ */
967
+ export type CategoryAccessScope = "owner_only" | "vault_members";
943
968
  export interface CompositeApplied {
944
969
  /** Все шаги allow/bypass — цепочка применена сразу, без заявки */
945
970
  applied: true;
@@ -1225,7 +1250,7 @@ export interface CreateAddressBody {
1225
1250
  * length: 1..16
1226
1251
  */
1227
1252
  emoji?: string;
1228
- /** Создать адрес В группу адресов (нужно право create_address на группе). Новый адрес — member группы, доступен её участникам */
1253
+ /** Создать адрес В группу адресов (нужно право create_address на группе). Новый адрес — member группы, доступен её участникам. Если создание ушло на подпись (202), папка сохраняется в заявке, а право на неё перепроверяется при применении: отозвали — адрес создаётся без папки, с оговоркой applyWarnings в ответе голосования. При прямом создании (201) то же — оговорка `warnings` в теле ответа */
1229
1254
  groupId?: string;
1230
1255
  }
1231
1256
  export interface CreateAddressGroupBody {
@@ -1249,6 +1274,14 @@ export interface CreateAddressResult {
1249
1274
  label: string | null;
1250
1275
  description: string | null;
1251
1276
  };
1277
+ /** Создано, но не всё: адрес есть, а в папку из `groupId` не положен. Право на папку роут проверяет до создания (иначе 403), а запись членства — уже после Wallet Service и мониторинга; право могли отозвать в этом окне или запись могла не пройти. Есть только когда непусто; не показывать адрес в папке, если оговорка пришла */
1278
+ warnings?: FolderSkipWarning[];
1279
+ }
1280
+ export interface FolderSkipWarning {
1281
+ /** FOLDER_NOT_FOUND — папку удалили; FOLDER_FORBIDDEN — у создателя адреса больше нет права заводить в неё адреса; FOLDER_WRITE_FAILED — сбой записи */
1282
+ code: "FOLDER_NOT_FOUND" | "FOLDER_FORBIDDEN" | "FOLDER_WRITE_FAILED";
1283
+ /** Готовая фраза для человека */
1284
+ message: string;
1252
1285
  }
1253
1286
  export interface CreateCompositeBody {
1254
1287
  /**
@@ -1729,6 +1762,8 @@ export type EvaluationResult = {
1729
1762
  export interface PolicyTrace {
1730
1763
  policyId: string;
1731
1764
  policyCode: string;
1765
+ /** Редакция политики, которая оценивалась (Ф4) — та же, что попадёт в `decisionPolicyVersionId` перевода. null — у политики ещё нет версий */
1766
+ policyVersionId: string | null;
1732
1767
  policyName: string;
1733
1768
  effect: string;
1734
1769
  applied: boolean;
@@ -2399,20 +2434,36 @@ export interface Participant {
2399
2434
  userId: string;
2400
2435
  user: UserCard | null;
2401
2436
  role: ShareRole;
2402
- /**
2403
- * ОТКУДА доступ — и, следовательно, что делать, чтобы его убрать:
2404
- * - {kind:"direct", grantId} — прямой грант, отзывается точечно
2405
- * - {kind:"user_group", groupId, groupName} — грант выдан ГРУППЕ пользователей: точечно не отзывается, нужно вывести человека из группы
2406
- * - {kind:"address_group", groupId, groupName} — доступ через папку, куда входит адрес
2407
- *
2408
- * Источников может быть НЕСКОЛЬКО сразу; итоговая роль — максимум прав
2409
- */
2410
- via: Array<{
2411
- [key: string]: unknown;
2412
- }>;
2437
+ /** Источников может быть НЕСКОЛЬКО сразу; итоговая роль — максимум прав */
2438
+ via: ParticipantVia[];
2413
2439
  /** true — есть прямой грант, участника можно убрать одной операцией */
2414
2440
  removable: boolean;
2415
2441
  }
2442
+ /**
2443
+ * ОТКУДА доступ — и, следовательно, что делать, чтобы его убрать:
2444
+ * - direct — прямой грант, отзывается точечно
2445
+ * - user_group — грант выдан ГРУППЕ пользователей: точечно не отзывается, нужно вывести человека из группы
2446
+ * - address_group — доступ через папку, куда входит адрес (у участников самой папки не встречается)
2447
+ */
2448
+ export type ParticipantVia = {
2449
+ kind: "direct";
2450
+ /** Грант на сам объект — отзывается точечно */
2451
+ grantId: string;
2452
+ } | {
2453
+ kind: "user_group";
2454
+ /** Грант, выданный группе пользователей */
2455
+ grantId: string;
2456
+ groupId: string;
2457
+ /** Имя группы — чтобы объяснить «доступ у вас от группы Казначеи» */
2458
+ groupName: string;
2459
+ } | {
2460
+ kind: "address_group";
2461
+ /** Грант, выданный на папку */
2462
+ grantId: string;
2463
+ /** Папка, в которой лежит адрес */
2464
+ groupId: string;
2465
+ groupName: string;
2466
+ };
2416
2467
  /** Кто имеет доступ к объекту — списком ЛЮДЕЙ, а не грантов. Админы не включаются: их доступ надзорный, есть ко всему сразу и совместным доступом не является */
2417
2468
  export interface ParticipantsResponse {
2418
2469
  /** Владелец объекта: у адреса — создатель, у папки — менеджер. Не участник */
@@ -2634,6 +2685,7 @@ export interface PendingApproval {
2634
2685
  }
2635
2686
  export interface PolicyApprover {
2636
2687
  id: string;
2688
+ policyId: string;
2637
2689
  /** "user" | "group" */
2638
2690
  kind: string;
2639
2691
  userId: string | null;
@@ -2667,6 +2719,14 @@ export interface PolicyDetail {
2667
2719
  createdAt: string;
2668
2720
  /** format: date-time */
2669
2721
  updatedAt: string;
2722
+ /**
2723
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
2724
+ *
2725
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
2726
+ *
2727
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
2728
+ */
2729
+ currentVersionId: string | null;
2670
2730
  /** LEGACY: движок их не читает, решает conditionJson */
2671
2731
  rules: Rule[];
2672
2732
  /** Явные одобряющие (для allow-политики с approval{}) */
@@ -2809,6 +2869,14 @@ export interface Policy {
2809
2869
  createdAt: string;
2810
2870
  /** format: date-time */
2811
2871
  updatedAt: string;
2872
+ /**
2873
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
2874
+ *
2875
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
2876
+ *
2877
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
2878
+ */
2879
+ currentVersionId: string | null;
2812
2880
  }
2813
2881
  export interface PolicyTableItem {
2814
2882
  /** cuid */
@@ -2826,10 +2894,6 @@ export interface PolicyTableItem {
2826
2894
  adminBypass: boolean;
2827
2895
  allowInitiatorApproval: boolean;
2828
2896
  enabled: boolean;
2829
- /** Условие применимости — булево дерево. ЭТО ЧИТАЕТ ДВИЖОК. null — политика применима всегда. Legacy-таблица rules движком не используется и удаляется в Ф5 */
2830
- conditionJson: PolicyCondition | null;
2831
- /** Требование подписи. null — политика разрешает без одобрения */
2832
- approvalJson: PolicyApprovalInput | null;
2833
2897
  /** format: date-time */
2834
2898
  createdAt: string;
2835
2899
  /** format: date-time */
@@ -2888,6 +2952,14 @@ export interface PolicyWithRules {
2888
2952
  createdAt: string;
2889
2953
  /** format: date-time */
2890
2954
  updatedAt: string;
2955
+ /**
2956
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
2957
+ *
2958
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
2959
+ *
2960
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
2961
+ */
2962
+ currentVersionId: string | null;
2891
2963
  /** LEGACY: движок их не читает, решает conditionJson */
2892
2964
  rules: Rule[];
2893
2965
  /** Явные одобряющие (для allow-политики с approval{}) */
@@ -2955,6 +3027,8 @@ export interface RequestVoteResult {
2955
3027
  resolvedResultId: string | null;
2956
3028
  /** Только для композитных заявок: прогресс по шагам */
2957
3029
  stepsProgress?: StepProgress[];
3030
+ /** Применено, но не всё: адрес по заявке create_address создан (resolvedResultId), а в папку, выбранную при подаче, не положен. Право на папку проверяется при ПРИМЕНЕНИИ, а не при подаче. Есть только когда непусто; в истории заявки — событие folder_skipped */
3031
+ applyWarnings?: FolderSkipWarning[];
2958
3032
  }
2959
3033
  export interface StepProgress {
2960
3034
  /** range: -9007199254740991..9007199254740991 */
@@ -2997,20 +3071,39 @@ export interface SeatCreated {
2997
3071
  /** format: date-time */
2998
3072
  expiresAt: string;
2999
3073
  }
3074
+ export interface ShareBatchAllRejected {
3075
+ /** items: 1..∞ */
3076
+ results: ShareBatchRejectedItem[];
3077
+ }
3078
+ export interface ShareBatchRejectedItem {
3079
+ subjectType: "user" | "group";
3080
+ subjectUserId: string | null;
3081
+ subjectGroupId: string | null;
3082
+ ok: false;
3083
+ outcome: null;
3084
+ grant: null;
3085
+ error: {
3086
+ /** SELF_SHARE — себе; OWNER_SUBJECT — владельцу адреса; NOT_MEMBER — не участник Vault'а; NOT_VISIBLE — получатель вам не виден (нет гранта видимости); GROUP_NOT_FOUND — группы нет в этом Vault'е */
3087
+ code: string;
3088
+ /** Сейчас дублирует code — человеческий текст собирает клиент */
3089
+ message: string;
3090
+ };
3091
+ }
3092
+ export interface ShareBatchGrantedItem {
3093
+ subjectType: "user" | "group";
3094
+ subjectUserId: string | null;
3095
+ subjectGroupId: string | null;
3096
+ ok: true;
3097
+ /** created — новый грант; updated — у действующего гранта сменили права; reactivated — снят отзыв с ранее отозванного */
3098
+ outcome: "created" | "updated" | "reactivated";
3099
+ grant: AddressAccessGrant;
3100
+ error: null;
3101
+ }
3102
+ export type ShareBatchItem = ShareBatchGrantedItem | ShareBatchRejectedItem;
3000
3103
  export interface ShareBatchResult {
3001
- results: Array<{
3002
- subjectType: "user" | "group";
3003
- subjectUserId: string | null;
3004
- subjectGroupId: string | null;
3005
- ok: boolean;
3006
- outcome: "created" | "updated" | "reactivated" | null;
3007
- grant: AddressAccessGrant | null;
3008
- error: {
3009
- code: string;
3010
- message: string;
3011
- } | null;
3012
- }>;
3104
+ results: ShareBatchItem[];
3013
3105
  }
3106
+ export type ShareGrantCreated = AddressAccessGrant | ShareBatchResult;
3014
3107
  export interface Token {
3015
3108
  /** cuid */
3016
3109
  id: string;
@@ -3121,6 +3214,16 @@ export interface TransactionDetail {
3121
3214
  amlScore: number | null;
3122
3215
  /** Когда провайдер сделал проверку */
3123
3216
  amlCheckedAt: string | null;
3217
+ /** USD-оценка перевода: amountDecimal × usdRate, decimal-СТРОКА. Считается один раз при создании и live-курсом не пересчитывается; null — курс был недоступен */
3218
+ usdValue: string | null;
3219
+ /** Редакция политики, принявшая решение (PolicyVersion.id, Ф4). ЕДИНСТВЕННЫЙ способ связать перевод с правилами, которые действовали в момент решения: `decisionPolicyId` ведёт на политику, а её с тех пор могли переписать. Снимок — GET /policies/{id}/versions. null — решение принято до введения версий */
3220
+ decisionPolicyVersionId: string | null;
3221
+ /** Группы контрагентов получателя НА МОМЕНТ РЕШЕНИЯ (id групп). Версия политики хранит id групп белого списка, но не их состав — без этого снимка нельзя объяснить, почему платёж прошёл, после того как контрагента из группы убрали. null — получатель не был контрагентом */
3222
+ decisionCounterpartyGroupsJson: string[] | null;
3223
+ /** Контрагент, сопоставленный по toAddress при создании. Дублирует `counterparty.id` — отдаётся как колонка перевода */
3224
+ counterpartyId: string | null;
3225
+ /** Клиентский ключ попытки, с которым перевод создан (см. тело POST /transfers). null — ключ не передавали */
3226
+ idempotencyKey: string | null;
3124
3227
  amountDecimal: string;
3125
3228
  assetSymbol: string;
3126
3229
  }
@@ -3211,7 +3314,7 @@ export interface UserProfile {
3211
3314
  color: string | null;
3212
3315
  /** Emoji-иконка */
3213
3316
  emoji: string | null;
3214
- /** true — аккаунт создан по пригласительной ссылке и настоящая почта ещё НЕ указана (в email лежит технический адрес). Клиент обязан попросить адрес: пока его нет, человеку некуда прислать magic-link, и при истечении сессии он потеряет доступ. Привязка — POST /me/email */
3317
+ /** true — аккаунт создан по пригласительной ссылке и настоящая почта ещё НЕ указана (в email лежит технический адрес). Клиент обязан попросить адрес: пока его нет, человеку некуда прислать magic-link, и при истечении сессии он потеряет доступ. Привязка — POST /me/profile/email. Приходит и в GET, и в ответе PATCH */
3215
3318
  needsEmail: boolean;
3216
3319
  /** format: date-time */
3217
3320
  createdAt: string;
@@ -3297,6 +3400,7 @@ export interface VaultPoliciesPage {
3297
3400
  items: Array<{
3298
3401
  /** cuid */
3299
3402
  id: string;
3403
+ groupId: string;
3300
3404
  code: string;
3301
3405
  name: string;
3302
3406
  description: string | null;
@@ -3318,11 +3422,17 @@ export interface VaultPoliciesPage {
3318
3422
  createdAt: string;
3319
3423
  /** format: date-time */
3320
3424
  updatedAt: string;
3425
+ /**
3426
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
3427
+ *
3428
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
3429
+ *
3430
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
3431
+ */
3432
+ currentVersionId: string | null;
3321
3433
  _count: {
3322
3434
  /** range: -9007199254740991..9007199254740991 */
3323
3435
  rules: number;
3324
- /** range: -9007199254740991..9007199254740991 */
3325
- limits?: number;
3326
3436
  };
3327
3437
  /** Owner-группа политики */
3328
3438
  group: GroupRef;
@@ -3369,8 +3479,12 @@ export interface VisibilityGrant {
3369
3479
  } | null;
3370
3480
  status: string;
3371
3481
  grantorId: string;
3482
+ revokedAt: string | null;
3483
+ revokedById: string | null;
3372
3484
  /** format: date-time */
3373
3485
  createdAt: string;
3486
+ /** format: date-time */
3487
+ updatedAt: string;
3374
3488
  }
3375
3489
  export interface VoteBody {
3376
3490
  /** reject = вето: перевод сразу rejected + release средств */
@@ -3440,35 +3554,106 @@ export type WebhookResult = {
3440
3554
  duplicate: true;
3441
3555
  eventId: string;
3442
3556
  };
3443
- export interface ActivityReport {
3557
+ export interface AccountStatementReport {
3444
3558
  report: ReportMeta;
3445
- summary?: ReportSummary;
3446
- byAsset?: ByAssetRow[];
3447
- byCounterparty?: ByCounterpartyRow[];
3448
- byOwnAddress?: ByOwnAddressRow[];
3449
- byStatus?: ByStatusRow[];
3450
- byCategory?: ByCategoryRow[];
3451
- byAddressOwner?: ByAddressOwnerRow[];
3452
- timeline?: TimelinePoint[];
3453
- approvals?: ReportApprovals;
3454
- observations?: Observation[];
3455
- dataQuality?: DataQuality;
3456
- comparison?: Comparison | null;
3559
+ balancesByAsset: StatementTokenRow[];
3560
+ timeline: Array<{
3561
+ /** Начало интервала (как periodStart у остальных) */
3562
+ period: string;
3563
+ /** Задел; сейчас всегда пустая строка */
3564
+ opening: string;
3565
+ /** Приток ТЕКСТОМ для печати: "1.5 USDT; 20 TRX" — сумма с символом, активы через «; ». Числом не разбирать. Перечислен КАЖДЫЙ актив, по которому в интервале были операции, даже если притока по нему не было: тогда он приходит как "0 USDT", а не пропадает */
3566
+ incoming: string;
3567
+ /** Отток, в том же текстовом виде и с теми же нулевыми позициями */
3568
+ outgoing: string;
3569
+ /** Задел; сейчас всегда "0" */
3570
+ adjustments: string;
3571
+ /** Задел; сейчас всегда пустая строка */
3572
+ closing: string;
3573
+ }>;
3574
+ dataQuality: DataQuality;
3457
3575
  }
3458
3576
  export interface ReportMeta {
3459
- id?: string | null;
3460
- /** snake_case: transaction_activity и т.д. */
3461
- type: string;
3462
- from: string | null;
3463
- to: string | null;
3464
- timezone: string | null;
3465
- dateBasis?: string | null;
3466
- baseCurrency?: string | null;
3467
- classificationMode?: string | null;
3468
- dataCutoff?: string | null;
3469
- schemaVersion?: string | number | null;
3577
+ /** Задел под сохранённый отчёт. У живого отчёта всегда null */
3578
+ id: string | null;
3579
+ /** Тип отчёта, snake_case — тот же, что в пути и в ?type= */
3580
+ type: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics";
3470
3581
  generatedAt: string;
3471
- generatedById?: string | null;
3582
+ /** Кто запросил отчёт */
3583
+ generatedById: string;
3584
+ /** Начало периода (включительно), ISO-8601 UTC. У balance_position from = to = момент позиции */
3585
+ from: string;
3586
+ /** Конец периода (НЕ включительно), ISO-8601 UTC */
3587
+ to: string;
3588
+ /** IANA-зона, в которой нарезан timeline. У balance_position всегда UTC */
3589
+ timezone: string;
3590
+ /** По какой дате операция отнесена к периоду. Если параметр не прислан: blockchainAt у counterparty_activity и counterparty_group_activity, createdAt у остальных */
3591
+ dateBasis: "createdAt" | "blockchainAt" | "confirmedAt";
3592
+ baseCurrency: "USD";
3593
+ /** Значение параметра counterpartyClassification (по умолчанию historical). У balance_position всегда current */
3594
+ classificationMode: "current" | "historical";
3595
+ dataCutoff: {
3596
+ /** Операции новее в отчёт не вошли */
3597
+ timestamp: string;
3598
+ /** Задел; сейчас всегда null */
3599
+ blockNumber: number | null;
3600
+ };
3601
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
3602
+ schemaVersion: string;
3603
+ }
3604
+ export interface StatementTokenRow {
3605
+ tokenId: string;
3606
+ assetCode: string;
3607
+ assetSymbol: string;
3608
+ /** Network.slug */
3609
+ network: string;
3610
+ openingAvailable: string;
3611
+ openingReserved: string;
3612
+ openingTotal: string;
3613
+ incomingExternal: string;
3614
+ outgoingExternal: string;
3615
+ incomingInternal: string;
3616
+ outgoingInternal: string;
3617
+ /** Ручные корректировки ledger (kind=adjustment) за период */
3618
+ adjustments: string;
3619
+ closingAvailable: string;
3620
+ closingReserved: string;
3621
+ closingTotal: string;
3622
+ /** (opening + движения + adjustments) − closing из ledger. Ожидается "0"; ненулевое значение — операции и ledger разошлись, и оно же считается в dataQuality.ledgerReconciliationDifferences */
3623
+ reconciliationDifference: string;
3624
+ }
3625
+ export interface DataQuality {
3626
+ totalOperations: number;
3627
+ unmatchedOperations: number;
3628
+ retroactivelyMatchedOperations: number;
3629
+ operationsWithoutUsdRate: number;
3630
+ operationsWithoutSelectedDate: number;
3631
+ operationsWithoutBlockchainTime: number;
3632
+ operationsWithoutPaymentPurpose: number;
3633
+ operationsWithoutBusinessReference: number;
3634
+ uncategorizedOperations: number;
3635
+ conflictingCounterpartyClassifications: number;
3636
+ invalidOperationRelations: number;
3637
+ missingLedgerEntries: number;
3638
+ ledgerReconciliationDifferences: number;
3639
+ }
3640
+ /** УСТАРЕЛО: используйте схему конкретного отчёта (TransactionActivityReport, AssetMovementReport, …) или AnyReport. Имя сохранено для совместимости сгенерированных клиентов: это объединение девяти отчётов, кроме category_analytics */
3641
+ export type ActivityReport = TransactionActivityReport | CounterpartyActivityReport | CounterpartyGroupActivityReport | AssetMovementReport | AddressActivityReport | AccountStatementReport | OperationsExceptionsReport | ApprovalActivityReport | BalancePositionReport;
3642
+ export interface TransactionActivityReport {
3643
+ report: ReportMeta;
3644
+ summary: ReportSummary;
3645
+ byAsset: ByAssetRow[];
3646
+ byCounterparty: ByCounterpartyRow[];
3647
+ byOwnAddress: ByOwnAddressRow[];
3648
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
3649
+ byAddressOwner?: ByAddressOwnerRow[];
3650
+ byStatus: ByStatusRow[];
3651
+ byCategory: ByCategoryRow[];
3652
+ timeline: TimelinePoint[];
3653
+ approvals: ReportApprovals;
3654
+ comparison: Comparison;
3655
+ observations: Observation[];
3656
+ dataQuality: DataQuality;
3472
3657
  }
3473
3658
  export interface ReportSummary {
3474
3659
  operationsCount: number;
@@ -3498,30 +3683,37 @@ export interface ReportSummary {
3498
3683
  minimumAmountByAsset: AssetAmount[];
3499
3684
  /** @defaultValue [] */
3500
3685
  maximumAmountByAsset: AssetAmount[];
3501
- confirmedCount?: number;
3502
- pendingCount?: number;
3503
- awaitingApprovalCount?: number;
3504
- rejectedCount?: number;
3505
- cancelledCount?: number;
3506
- failedCount?: number;
3507
- activeDays?: number | null;
3508
- firstOperationAt?: string | null;
3509
- lastOperationAt?: string | null;
3686
+ confirmedCount: number;
3687
+ pendingCount: number;
3688
+ awaitingApprovalCount: number;
3689
+ rejectedCount: number;
3690
+ cancelledCount: number;
3691
+ failedCount: number;
3692
+ /** Число календарных дней (в timezone отчёта) с хотя бы одной операцией */
3693
+ activeDays: number;
3694
+ /** createdAt самой ранней операции выборки; null — выборка пуста */
3695
+ firstOperationAt: string | null;
3696
+ /** createdAt самой поздней операции выборки; null — выборка пуста */
3697
+ lastOperationAt: string | null;
3510
3698
  }
3511
3699
  export interface AssetAmount {
3512
- tokenId: string | null;
3513
- assetCode: string | null;
3700
+ tokenId: string;
3701
+ /** Token.code из реестра */
3702
+ assetCode: string;
3514
3703
  assetSymbol: string;
3515
- network: string | null;
3516
- contractAddress?: string | null;
3517
- /** raw smallest units, строкой */
3704
+ /** Network.slug */
3705
+ network: string;
3706
+ /** DECIMAL-строка в единицах актива ("1.5"), НЕ raw smallest units; хвостовые нули убраны. Со знаком только у netFlow* ("-12.5" — отток больше притока), у остальных блоков неотрицательна */
3518
3707
  amount: string;
3519
3708
  }
3520
3709
  export interface ByAssetRow {
3521
- tokenId: string | null;
3522
- assetCode: string | null;
3710
+ tokenId: string;
3711
+ /** Token.code из реестра */
3712
+ assetCode: string;
3523
3713
  assetSymbol: string;
3524
- network: string | null;
3714
+ /** Network.slug */
3715
+ network: string;
3716
+ /** null у нативного актива сети */
3525
3717
  contractAddress: string | null;
3526
3718
  incoming: string;
3527
3719
  outgoing: string;
@@ -3544,8 +3736,11 @@ export interface ByAssetRow {
3544
3736
  maximumAmount: string | null;
3545
3737
  }
3546
3738
  export interface ByCounterpartyRow {
3739
+ /** null — операции без сопоставленного контрагента (одна строка на все) */
3547
3740
  counterpartyId: string | null;
3741
+ /** Имя контрагента. У строки без контрагента (counterpartyId = null) сюда приходит английская заглушка "unmatched counterparty" — она не локализуется, поэтому различать такую строку надо по counterpartyId */
3548
3742
  name: string | null;
3743
+ /** Свободная категория контрагента (exchange, client…); null — не задана */
3549
3744
  kind: string | null;
3550
3745
  /** @defaultValue [] */
3551
3746
  incomingByAsset: AssetAmount[];
@@ -3566,7 +3761,7 @@ export interface ByOwnAddressRow {
3566
3761
  [key: string]: unknown;
3567
3762
  } | null;
3568
3763
  /**
3569
- * Папки, в которых адрес состоял (зависит от folderClassification)
3764
+ * Задел под папки адреса. Сейчас ВСЕГДА пустой массив
3570
3765
  * @defaultValue []
3571
3766
  */
3572
3767
  folderSnapshots: unknown[];
@@ -3578,6 +3773,19 @@ export interface ByOwnAddressRow {
3578
3773
  grossTurnoverUsd: string | null;
3579
3774
  operationsCount: number;
3580
3775
  }
3776
+ export interface ByAddressOwnerRow {
3777
+ /** Владелец адресов (создатель) */
3778
+ userId: string;
3779
+ /** Считается от вызывающего: у двух людей один отчёт разложится по-разному */
3780
+ isMe: boolean;
3781
+ /** Сколько адресов этого владельца попало в выборку */
3782
+ addressesCount: number;
3783
+ incomingByAsset: AssetAmount[];
3784
+ outgoingByAsset: AssetAmount[];
3785
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3786
+ grossTurnoverUsd: string | null;
3787
+ operationsCount: number;
3788
+ }
3581
3789
  export interface ByStatusRow {
3582
3790
  status: string;
3583
3791
  count: number;
@@ -3596,16 +3804,6 @@ export interface ByCategoryRow {
3596
3804
  /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3597
3805
  usdValue: string | null;
3598
3806
  }
3599
- export interface ByAddressOwnerRow {
3600
- ownerId: string | null;
3601
- owner?: {
3602
- [key: string]: unknown;
3603
- } | null;
3604
- isMe: boolean;
3605
- operationsCount: number;
3606
- /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3607
- grossTurnoverUsd: string | null;
3608
- }
3609
3807
  export interface TimelinePoint {
3610
3808
  periodStart: string;
3611
3809
  periodEnd: string;
@@ -3633,71 +3831,442 @@ export interface ReportApprovals {
3633
3831
  medianApprovalSeconds: number | null;
3634
3832
  maximumApprovalSeconds: number | null;
3635
3833
  }
3834
+ export interface Comparison {
3835
+ /** none | previous_period | previous_year */
3836
+ mode: string;
3837
+ previousFrom: string | null;
3838
+ previousTo: string | null;
3839
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
3840
+ incomingUsdChangePercent: string | null;
3841
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
3842
+ outgoingUsdChangePercent: string | null;
3843
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
3844
+ grossTurnoverUsdChangePercent: string | null;
3845
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
3846
+ operationsCountChangePercent: string | null;
3847
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
3848
+ averageOperationUsdChangePercent: string | null;
3849
+ }
3636
3850
  export interface Observation {
3851
+ /** Машиночитаемый код: COUNTERPARTY_TURNOVER_INCREASED, SINGLE_OPERATION_CONCENTRATION, UNMATCHED_OPERATIONS_PRESENT… */
3637
3852
  code: string;
3638
- severity: string;
3853
+ severity: "info" | "warning";
3639
3854
  title: string;
3640
3855
  description: string;
3641
3856
  metric: string | null;
3642
3857
  currentValue: string | null;
3858
+ /** Задел; сейчас всегда null */
3643
3859
  comparisonValue: string | null;
3644
- changePercent: number | null;
3645
- drilldownFilters?: {
3860
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — наблюдение не про сравнение периодов (концентрация, качество данных, зависшие операции). Не null только у наблюдений о росте или падении оборота и среднего платежа — там это то же значение, что в comparison */
3861
+ changePercent: string | null;
3862
+ /** Готовое уточнение для drill-down, раскрывающее наблюдение до операций; {} — раскрывать нечем */
3863
+ drilldownFilters: {
3646
3864
  [key: string]: unknown;
3647
- } | null;
3865
+ };
3648
3866
  }
3649
- export interface DataQuality {
3650
- totalOperations: number;
3651
- unmatchedOperations: number;
3652
- retroactivelyMatchedOperations: number;
3653
- operationsWithoutUsdRate: number;
3654
- operationsWithoutSelectedDate: number;
3655
- operationsWithoutBlockchainTime: number;
3656
- operationsWithoutPaymentPurpose: number;
3657
- operationsWithoutBusinessReference: number;
3658
- uncategorizedOperations: number;
3659
- conflictingCounterpartyClassifications: number;
3660
- invalidOperationRelations: number;
3661
- missingLedgerEntries: number;
3662
- ledgerReconciliationDifferences: number;
3867
+ export interface CounterpartyActivityReport {
3868
+ report: ReportMeta;
3869
+ /** Карточка контрагента на момент построения отчёта */
3870
+ counterparty: {
3871
+ id: string;
3872
+ name: string;
3873
+ kind: string | null;
3874
+ /** Архивный контрагент отчёт не блокирует — история по нему остаётся */
3875
+ archived: boolean;
3876
+ /** Адресов в справочнике у контрагента */
3877
+ addressesCount: number;
3878
+ /** Группы контрагентов, в которых он состоит СЕЙЧАС */
3879
+ groupIds: string[];
3880
+ };
3881
+ summary: ReportSummary;
3882
+ byAsset: ByAssetRow[];
3883
+ byCounterpartyAddress: ByCounterpartyAddressRow[];
3884
+ byOwnAddress: ByOwnAddressRow[];
3885
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
3886
+ byAddressOwner?: ByAddressOwnerRow[];
3887
+ byStatus: ByStatusRow[];
3888
+ byCategory: ByCategoryRow[];
3889
+ timeline: TimelinePoint[];
3890
+ approvals: ReportApprovals;
3891
+ comparison: Comparison;
3892
+ observations: Observation[];
3893
+ dataQuality: DataQuality;
3894
+ }
3895
+ export interface ByCounterpartyAddressRow {
3896
+ /** Запись справочника адресов контрагента; null — адрес в справочник не внесён */
3897
+ counterpartyAddressId: string | null;
3898
+ /** Внешний адрес (on-chain) */
3899
+ address: string;
3900
+ /** Network.slug */
3901
+ network: string;
3902
+ /** Задел; сейчас всегда null */
3903
+ label: string | null;
3904
+ incomingByAsset: AssetAmount[];
3905
+ outgoingByAsset: AssetAmount[];
3906
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3907
+ grossTurnoverUsd: string | null;
3908
+ operationsCount: number;
3909
+ incomingCount: number;
3910
+ outgoingCount: number;
3911
+ /** Доля адреса в USD-обороте отчёта, проценты decimal-строкой; null — нет USD-оценки или оборот нулевой */
3912
+ turnoverShareUsd: string | null;
3913
+ firstOperationAt: string | null;
3914
+ lastOperationAt: string | null;
3915
+ /** true — до начала периода операций с этим адресом не было (в любом статусе, включая внутренние потоки) */
3916
+ isNewInPeriod: boolean;
3917
+ }
3918
+ export interface CounterpartyGroupActivityReport {
3919
+ report: ReportMeta;
3920
+ group: {
3921
+ id: string;
3922
+ code: string;
3923
+ name: string;
3924
+ /** Сколько контрагентов в группе СЕЙЧАС. При counterpartyClassification=historical выборка идёт по составу на момент отчёта и может не совпасть с этим числом */
3925
+ counterpartiesCount: number;
3926
+ };
3927
+ summary: ReportSummary;
3928
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
3929
+ byAddressOwner?: ByAddressOwnerRow[];
3930
+ byCounterparty: ByGroupCounterpartyRow[];
3931
+ byAsset: ByAssetRow[];
3932
+ byStatus: ByStatusRow[];
3933
+ byCategory: ByCategoryRow[];
3934
+ timeline: TimelinePoint[];
3935
+ approvals: ReportApprovals;
3936
+ comparison: Comparison;
3937
+ observations: Observation[];
3938
+ dataQuality: DataQuality;
3939
+ }
3940
+ export interface ByGroupCounterpartyRow {
3941
+ counterpartyId: string | null;
3942
+ name: string | null;
3943
+ kind: string | null;
3944
+ incomingByAsset: AssetAmount[];
3945
+ outgoingByAsset: AssetAmount[];
3946
+ /** Приток минус отток по активу, со знаком */
3947
+ netFlowByAsset: AssetAmount[];
3948
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3949
+ grossTurnoverUsd: string | null;
3950
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3951
+ netFlowUsd: string | null;
3952
+ operationsCount: number;
3953
+ /** Доля контрагента в USD-обороте группы, проценты decimal-строкой ("37.5"); null — у оборота нет USD-оценки или он нулевой */
3954
+ shareOfGroupTurnoverUsd: string | null;
3955
+ firstOperationAt: string | null;
3956
+ lastOperationAt: string | null;
3663
3957
  }
3664
- export interface Comparison {
3665
- mode: string;
3666
- previousFrom: string | null;
3667
- previousTo: string | null;
3668
- incomingUsdChangePercent: number | null;
3669
- outgoingUsdChangePercent: number | null;
3670
- grossTurnoverUsdChangePercent: number | null;
3671
- operationsCountChangePercent: number | null;
3672
- averageOperationUsdChangePercent: number | null;
3958
+ export interface AssetMovementReport {
3959
+ report: ReportMeta;
3960
+ asset: {
3961
+ tokenId: string;
3962
+ /** Token.code из реестра */
3963
+ code: string;
3964
+ symbol: string;
3965
+ name: string;
3966
+ decimals: number;
3967
+ /** Network.slug */
3968
+ network: string;
3969
+ /** null у нативного актива сети */
3970
+ contractAddress: string | null;
3971
+ };
3972
+ /** Остаток актива на начало периода (from), из ledger. DECIMAL в единицах актива — не raw, в отличие от openingBalances у address_activity */
3973
+ openingBalance: {
3974
+ /** decimal */
3975
+ available: string;
3976
+ /** decimal */
3977
+ reserved: string;
3978
+ /** available + reserved, decimal */
3979
+ total: string;
3980
+ };
3981
+ /** Остаток актива на конец периода (to), из ledger. DECIMAL в единицах актива — не raw, в отличие от openingBalances у address_activity */
3982
+ closingBalance: {
3983
+ /** decimal */
3984
+ available: string;
3985
+ /** decimal */
3986
+ reserved: string;
3987
+ /** available + reserved, decimal */
3988
+ total: string;
3989
+ };
3990
+ summary: ReportSummary;
3991
+ byCounterparty: ByCounterpartyBriefRow[];
3992
+ byOwnAddress: ByOwnAddressRow[];
3993
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
3994
+ byAddressOwner?: ByAddressOwnerRow[];
3995
+ byStatus: ByStatusRow[];
3996
+ byCategory: ByCategoryRow[];
3997
+ timeline: TimelinePoint[];
3998
+ comparison: Comparison;
3999
+ observations: Observation[];
4000
+ dataQuality: DataQuality;
4001
+ }
4002
+ export interface ByCounterpartyBriefRow {
4003
+ /** null — операции без сопоставленного контрагента (одна строка на все) */
4004
+ counterpartyId: string | null;
4005
+ /** Имя контрагента. У строки без контрагента (counterpartyId = null) сюда приходит английская заглушка "unmatched counterparty" — она не локализуется, поэтому различать такую строку надо по counterpartyId */
4006
+ name: string | null;
4007
+ incomingByAsset: AssetAmount[];
4008
+ outgoingByAsset: AssetAmount[];
4009
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4010
+ grossTurnoverUsd: string | null;
4011
+ operationsCount: number;
3673
4012
  }
3674
- export interface BalancesAsOf {
3675
- /** Момент среза (echo параметра ?at=) */
4013
+ export interface AddressActivityReport {
4014
+ report: ReportMeta;
4015
+ /** Цель отчёта, и форма у неё ДВЕ: при addressId — карточка адреса, при folderId — {folder:{id,name}}. Имя ключа одно на оба случая исторически */
4016
+ address: {
4017
+ id: string;
4018
+ address: string;
4019
+ label: string | null;
4020
+ description: string | null;
4021
+ /** Без апострофов: m/44/195/… */
4022
+ derivationPath: string;
4023
+ segmentsJson: {
4024
+ [key: string]: unknown;
4025
+ };
4026
+ /** Network.slug */
4027
+ network: string;
4028
+ createdAt: string;
4029
+ } | {
4030
+ folder: {
4031
+ id: string;
4032
+ name: string;
4033
+ };
4034
+ };
4035
+ /** Папки адреса. Вторая дата зависит от folderClassification: historical (по умолчанию) — validFrom из истории членства, current — createdAt текущего членства. При folderId — всегда пустой массив */
4036
+ folderMemberships: Array<{
4037
+ folderId: string;
4038
+ validFrom: string;
4039
+ } | {
4040
+ folderId: string;
4041
+ createdAt: string;
4042
+ }>;
4043
+ /** Балансы счетов на начало периода (from), RAW. При addressId — счета этого адреса. При folderId выборка НЕ сужается до папки: приходят все счета scope вызывающего (у admin — весь Vault, включая external и adjustment) */
4044
+ openingBalances: ReportAccountBalance[];
4045
+ /** Балансы счетов на конец периода (to), RAW. При addressId — счета этого адреса. При folderId выборка НЕ сужается до папки: приходят все счета scope вызывающего (у admin — весь Vault, включая external и adjustment) */
4046
+ closingBalances: ReportAccountBalance[];
4047
+ summary: ReportSummary;
4048
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
4049
+ byAddressOwner?: ByAddressOwnerRow[];
4050
+ /** Доля адреса (папки) в USD-обороте всего scope за тот же период, проценты decimal-строкой; null — нет USD-оценки или оборот scope нулевой */
4051
+ shareOfUserActivityUsd: string | null;
4052
+ byAsset: ByAssetRow[];
4053
+ byCounterparty: ByCounterpartyBriefRow[];
4054
+ byStatus: ByStatusRow[];
4055
+ byCategory: ByCategoryRow[];
4056
+ timeline: TimelinePoint[];
4057
+ observations: Observation[];
4058
+ dataQuality: DataQuality;
4059
+ }
4060
+ export interface ReportAccountBalance {
4061
+ accountId: string;
4062
+ /** available / reserved — счета адресов; external / adjustment — счета уровня Vault'а (без адреса): встречная сторона двойной записи */
4063
+ purpose: "available" | "reserved" | "external" | "adjustment";
4064
+ tokenId: string;
4065
+ /** null у счетов уровня Vault'а */
4066
+ addressId: string | null;
4067
+ /** Raw smallest units со знаком — НЕ decimal */
4068
+ balanceRaw: string;
4069
+ }
4070
+ export interface OperationsExceptionsReport {
4071
+ report: ReportMeta;
4072
+ summary: {
4073
+ operationsCount: number;
4074
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4075
+ usdValue: string | null;
4076
+ /** null — выборка пуста */
4077
+ oldestOperationAt: string | null;
4078
+ /** Возраст считается от createdAt до момента построения отчёта */
4079
+ averageAgeSeconds: number | null;
4080
+ medianAgeSeconds: number | null;
4081
+ };
4082
+ /** Всегда шесть строк в этом порядке, включая пустые корзины */
4083
+ ageBuckets: Array<{
4084
+ bucket: "0-15m" | "15-60m" | "1-6h" | "6-24h" | "1-3d" | ">3d";
4085
+ count: number;
4086
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4087
+ usdValue: string | null;
4088
+ }>;
4089
+ byStatus: ByStatusRow[];
4090
+ byAsset: ByAssetRow[];
4091
+ byCounterparty: Array<{
4092
+ /** null — операции без сопоставленного контрагента (одна строка на все) */
4093
+ counterpartyId: string | null;
4094
+ /** Имя контрагента. У строки без контрагента (counterpartyId = null) сюда приходит английская заглушка "unmatched counterparty" — она не локализуется, поэтому различать такую строку надо по counterpartyId */
4095
+ name: string | null;
4096
+ count: number;
4097
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4098
+ usdValue: string | null;
4099
+ }>;
4100
+ byInitiator: Array<{
4101
+ /** null — у операции нет инициатора (депозиты) */
4102
+ initiatorId: string | null;
4103
+ count: number;
4104
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4105
+ usdValue: string | null;
4106
+ }>;
4107
+ byDecisionPolicy: Array<{
4108
+ /** null — решение по операции политика не принимала */
4109
+ decisionPolicyId: string | null;
4110
+ code: string | null;
4111
+ count: number;
4112
+ }>;
4113
+ dataQuality: DataQuality;
4114
+ }
4115
+ export interface ApprovalActivityReport {
4116
+ report: ReportMeta;
4117
+ summary: {
4118
+ requestsCount: number;
4119
+ pendingCount: number;
4120
+ approvedCount: number;
4121
+ rejectedCount: number;
4122
+ cancelledCount: number;
4123
+ /** approved / (approved + rejected) × 100, decimal-строка; null — решённых заявок нет. Отменённые в знаменатель не входят */
4124
+ approvalRatePercent: string | null;
4125
+ rejectionRatePercent: string | null;
4126
+ /** cancelled / все заявки × 100; null — заявок нет */
4127
+ cancellationRatePercent: string | null;
4128
+ /** От создания заявки до resolvedAt; null — решённых нет */
4129
+ averageResolutionSeconds: number | null;
4130
+ medianResolutionSeconds: number | null;
4131
+ maximumResolutionSeconds: number | null;
4132
+ /** От создания заявки до первого голоса; null — голосов нет */
4133
+ averageFirstVoteSeconds: number | null;
4134
+ medianFirstVoteSeconds: number | null;
4135
+ requestsWithoutVotes: number;
4136
+ };
4137
+ byAction: Array<{
4138
+ /** ApprovalRequest.action: transfer, composite… */
4139
+ action: string;
4140
+ count: number;
4141
+ approvedCount: number;
4142
+ rejectedCount: number;
4143
+ cancelledCount: number;
4144
+ pendingCount: number;
4145
+ averageResolutionSeconds: number | null;
4146
+ }>;
4147
+ byInitiator: Array<{
4148
+ userId: string;
4149
+ name: string;
4150
+ requestsCount: number;
4151
+ approvedCount: number;
4152
+ rejectedCount: number;
4153
+ cancelledCount: number;
4154
+ averageResolutionSeconds: number | null;
4155
+ }>;
4156
+ byApprover: Array<{
4157
+ userId: string;
4158
+ /** Имя; если пользователь не найден — его userId */
4159
+ name: string;
4160
+ assignedRequestsCount: number;
4161
+ votedCount: number;
4162
+ approveVotes: number;
4163
+ rejectVotes: number;
4164
+ pendingVotes: number;
4165
+ averageVoteSeconds: number | null;
4166
+ medianVoteSeconds: number | null;
4167
+ }>;
4168
+ byDecisionPolicy: Array<{
4169
+ decisionPolicyId: string | null;
4170
+ code: string | null;
4171
+ /** Задел; сейчас всегда null */
4172
+ name: string | null;
4173
+ triggeredCount: number;
4174
+ approvedCount: number;
4175
+ rejectedCount: number;
4176
+ pendingCount: number;
4177
+ averageResolutionSeconds: number | null;
4178
+ }>;
4179
+ timeline: Array<{
4180
+ periodStart: string;
4181
+ createdCount: number;
4182
+ /** approved + rejected; отменённые не входят */
4183
+ resolvedCount: number;
4184
+ approvedCount: number;
4185
+ rejectedCount: number;
4186
+ cancelledCount: number;
4187
+ }>;
4188
+ /** Только эхо параметра comparison: процентов изменения этот отчёт НЕ считает. У живого отчёта previous_year приходит как none; в снапшоте и при запуске шаблона значение не переводится и приходит как прислано */
4189
+ comparison: {
4190
+ mode: "none" | "previous_period" | "previous_year";
4191
+ };
4192
+ dataQuality: DataQuality;
4193
+ }
4194
+ export interface BalancePositionReport {
4195
+ report: ReportMeta;
4196
+ /** Момент позиции, ISO-8601 UTC */
3676
4197
  asOf: string;
3677
- /** @defaultValue [] */
3678
- totals: Array<{
3679
- [key: string]: unknown;
4198
+ totalsByAsset: Array<{
4199
+ tokenId: string;
4200
+ assetCode: string;
4201
+ assetSymbol: string;
4202
+ /** Network.slug */
4203
+ network: string;
4204
+ /** decimal */
4205
+ available: string;
4206
+ /** decimal */
4207
+ reserved: string;
4208
+ /** available + reserved, decimal */
4209
+ total: string;
4210
+ usdRate: string | null;
4211
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4212
+ usdValue: string | null;
3680
4213
  }>;
3681
- /** @defaultValue [] */
3682
- accounts: Array<{
3683
- [key: string]: unknown;
4214
+ byAddress: Array<{
4215
+ addressId: string;
4216
+ address: string;
4217
+ label: string | null;
4218
+ balances: Array<{
4219
+ tokenId: string;
4220
+ /** decimal */
4221
+ available: string;
4222
+ /** decimal */
4223
+ reserved: string;
4224
+ /** decimal */
4225
+ total: string;
4226
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4227
+ usdValue: string | null;
4228
+ }>;
3684
4229
  }>;
4230
+ concentration: {
4231
+ /** Доля крупнейшего адреса в USD, проценты decimal-строкой; null без USD-оценки */
4232
+ largestAddressShareUsd: string | null;
4233
+ /** Доля пяти крупнейших адресов, проценты decimal-строкой; null без USD-оценки */
4234
+ top5AddressesShareUsd: string | null;
4235
+ };
4236
+ dataQuality: DataQuality;
3685
4237
  }
4238
+ /** Один из десяти отчётов. Форму определяет report.type: у каждого типа своя схема, общих для всех блоков — только report и (кроме category_analytics) dataQuality */
4239
+ export type AnyReport = TransactionActivityReport | CounterpartyActivityReport | CounterpartyGroupActivityReport | AssetMovementReport | AddressActivityReport | AccountStatementReport | OperationsExceptionsReport | ApprovalActivityReport | BalancePositionReport | CategoryAnalytics;
3686
4240
  export interface CategoryAnalytics {
3687
- report: ReportMeta;
4241
+ report: {
4242
+ /** Тип отчёта, snake_case — тот же, что в пути и в ?type= */
4243
+ type: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics";
4244
+ generatedAt: string;
4245
+ /** Начало периода (включительно), ISO-8601 UTC. У balance_position from = to = момент позиции */
4246
+ from: string;
4247
+ /** Конец периода (НЕ включительно), ISO-8601 UTC */
4248
+ to: string;
4249
+ /** IANA-зона, в которой нарезан timeline. У balance_position всегда UTC */
4250
+ timezone: string;
4251
+ };
3688
4252
  categoryKey: string;
4253
+ /** Операции с активным назначением этой категории */
3689
4254
  operationsCount: number;
4255
+ /** null — metricField не задан */
3690
4256
  metric: {
3691
4257
  fieldKey: string;
3692
- agg: string;
4258
+ agg: "sum" | "avg" | "min" | "max" | "median" | "count";
3693
4259
  /** null, когда метрика деноминирована — см. byCurrency */
3694
4260
  value: string | null;
3695
4261
  byCurrency: {
3696
4262
  [key: string]: unknown;
3697
4263
  } | null;
3698
4264
  } | null;
4265
+ /** null — groupByField не задан */
3699
4266
  groups: Array<{
4267
+ /** Значение поля groupByField; null — у назначения поле не заполнено */
3700
4268
  key: string | null;
4269
+ /** Число НАЗНАЧЕНИЙ категории в группе, а не операций: у одной операции их может быть несколько, поэтому сумма по группам бывает больше operationsCount отчёта */
3701
4270
  operationsCount: number;
3702
4271
  metric: string | null;
3703
4272
  metricByCurrency: {
@@ -3705,6 +4274,74 @@ export interface CategoryAnalytics {
3705
4274
  } | null;
3706
4275
  }> | null;
3707
4276
  }
4277
+ export interface BalancesAsOfAccount {
4278
+ accountId: string;
4279
+ /** available / reserved — счета адресов; external / adjustment — счета уровня Vault'а (без адреса): встречная сторона двойной записи */
4280
+ purpose: "available" | "reserved" | "external" | "adjustment";
4281
+ tokenId: string;
4282
+ /** null у vault-level счетов */
4283
+ addressId: string | null;
4284
+ /** Raw smallest units со знаком (vault-level счета бывают отрицательными) */
4285
+ balance: string;
4286
+ /** То же в единицах актива. Если токена счёта нет в реестре — raw, как в balance */
4287
+ balanceDecimal: string;
4288
+ }
4289
+ export interface BalancesAsOf {
4290
+ /** Момент среза: параметр ?at=, приведённый к ISO-8601 в UTC (не буквальное эхо) */
4291
+ asOf: string;
4292
+ /** По строке на токен; складываются только available и reserved. Пустой массив — движений до момента среза не было (или ?purpose= выбрал external/adjustment): это «нет данных», а не «ноль» */
4293
+ totals: BalancesAsOfTotal[];
4294
+ /** Счета, по которым были движения до момента среза. ОТСУТСТВУЕТ при ?summary=true. Участник видит только счета адресов из своего scope; vault-level счета (external/adjustment) — только admin */
4295
+ accounts?: BalancesAsOfAccount[];
4296
+ }
4297
+ export interface BalancesAsOfTotal {
4298
+ token: {
4299
+ code: string;
4300
+ symbol: string;
4301
+ decimals: number;
4302
+ /** Network.slug */
4303
+ network: string;
4304
+ };
4305
+ tokenId: string;
4306
+ /** DECIMAL (не raw!) — дубль availableDecimal; у GET /balances это поле raw */
4307
+ available: string;
4308
+ availableDecimal: string;
4309
+ /** DECIMAL (не raw!) — дубль reservedDecimal; у GET /balances это поле raw */
4310
+ reserved: string;
4311
+ reservedDecimal: string;
4312
+ /** available + reserved, decimal: резерв — ещё не расход, пока перевод не подтверждён */
4313
+ ownedTotal: string;
4314
+ }
4315
+ export interface CreateReportExportBody {
4316
+ /** json — canonical JSON результата; csv — плоский реестр операций. xlsx и pdf схема принимает, а хендлер отклоняет: 400 REPORT_UNSUPPORTED_FORMAT */
4317
+ format: "json" | "csv" | "xlsx" | "pdf";
4318
+ /** Зарезервировано: сейчас не влияет на файл */
4319
+ locale?: string;
4320
+ /** Зарезервировано: сейчас не влияет на файл */
4321
+ timezone?: string;
4322
+ /** Зарезервировано: сейчас не влияет на файл (csv — всегда реестр, json — всегда результат) */
4323
+ includeOperationRegister?: boolean;
4324
+ }
4325
+ export interface CreateReportSnapshotBody {
4326
+ reportType: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics";
4327
+ /** Те же параметры, что уходили в живой отчёт query-строкой (from, to, timezone, фильтры, includeShared, цель отчёта). Сохраняются в снапшоте как есть — под именем `parametersJson` */
4328
+ parameters: {
4329
+ [key: string]: unknown;
4330
+ };
4331
+ }
4332
+ export interface CreateReportTemplateBody {
4333
+ /** length: 1..120 */
4334
+ name: string;
4335
+ /** length: 0..500 */
4336
+ description?: string;
4337
+ reportType: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics";
4338
+ /** Параметры отчёта, как в query живого отчёта. Вместо from/to можно задать relativePeriod {unit: day|week|month, offset} — даты вычислятся при запуске. В ответе лежат под именем `parametersJson` */
4339
+ parameters: {
4340
+ [key: string]: unknown;
4341
+ };
4342
+ /** true — только admin */
4343
+ isShared?: boolean;
4344
+ }
3708
4345
  export interface DrilldownResponse {
3709
4346
  /** Операции с полями и categoryAssignments (sensitive-значения отредактированы по правам: значение null + ключ в redactedFields) */
3710
4347
  items: Array<{
@@ -3719,92 +4356,241 @@ export interface DrilldownResponse {
3719
4356
  usdValue: string | null;
3720
4357
  };
3721
4358
  }
4359
+ export interface LedgerEntriesPage {
4360
+ items: LedgerEntry[];
4361
+ nextCursor: string | null;
4362
+ }
3722
4363
  export interface LedgerEntry {
3723
4364
  id: string;
3724
4365
  vaultId: string;
3725
- /** deposit | transfer | adjustment … */
4366
+ /** topup | transfer_reserve | transfer_confirm | transfer_refund | adjustment | genesis. Это НЕ хранимый kind: три вида переименованы на выходе (reserve → transfer_reserve, confirm → transfer_confirm, release → transfer_refund), остальные отдаются как есть. Фильтр ?entryType= понимает оба написания */
3726
4367
  entryType: string;
3727
- reference: string | null;
3728
- description: string | null;
3729
- operationType: string | null;
4368
+ /** Документ-источник; null у корректировок и genesis */
4369
+ operationType: "transfer" | "deposit" | null;
4370
+ /** id перевода или депозита — по operationType */
3730
4371
  operationId: string | null;
3731
- token: {
3732
- [key: string]: unknown;
3733
- } | null;
3734
- effectiveAt: string;
4372
+ /** Время записи в журнал. По нему идут сортировка, фильтр from/to и балансы as-of */
3735
4373
  postedAt: string;
4374
+ /** Экономическое время события (для on-chain — время блока) */
4375
+ effectiveAt: string;
4376
+ /** Ключ идемпотентности проводки (sourceKey), уникален: "topup:{eventId}", "reserve:{transferId}", "confirm:{transferId}", "release:{transferId}" */
4377
+ reference: string;
4378
+ /** Примечание к проводке (memo) */
4379
+ description: string | null;
4380
+ /** Токен проводки — по её ПЕРВОМУ постингу, взятому до фильтрации под права: присутствует, даже если postings у вызывающего пуст */
4381
+ token: LedgerEntryToken | null;
4382
+ postings: LedgerPosting[];
4383
+ /** То же значение, что postedAt */
3736
4384
  createdAt: string;
3737
- postings: Array<{
3738
- id: string;
3739
- accountId: string;
3740
- amount: string;
3741
- amountDecimal?: string;
3742
- direction?: string;
3743
- token?: {
3744
- [key: string]: unknown;
3745
- } | null;
3746
- account?: {
3747
- [key: string]: unknown;
3748
- } | null;
3749
- }>;
4385
+ }
4386
+ export interface LedgerEntryToken {
4387
+ id: string;
4388
+ code: string;
4389
+ symbol: string;
4390
+ decimals: number;
4391
+ /** Network.slug */
4392
+ network: string;
4393
+ /** null у нативного актива сети */
4394
+ contractAddress: string | null;
4395
+ }
4396
+ export interface LedgerPosting {
4397
+ id: string;
4398
+ accountId: string;
4399
+ /** available / reserved — счета адресов; external / adjustment — счета уровня Vault'а (без адреса): встречная сторона двойной записи */
4400
+ accountPurpose: "available" | "reserved" | "external" | "adjustment";
4401
+ /** address — счёт адреса; vault — счёт уровня Vault'а (external/adjustment) */
4402
+ ownerType: "address" | "vault";
4403
+ /** addressId для ownerType=address, иначе vaultId */
4404
+ ownerId: string;
4405
+ /** null у vault-level счетов */
4406
+ addressId: string | null;
4407
+ /** Raw smallest units со знаком: "2500000" | "-2500000" */
4408
+ amountRawSigned: string;
4409
+ /** Та же сумма в единицах актива, со знаком: "-2.5". Если токена постинга нет в реестре (чего при живом FK быть не должно), сюда попадает raw — значение совпадёт с amountRawSigned */
4410
+ amountDecimalSigned: string;
4411
+ }
4412
+ export interface PatchReportTemplateBody {
4413
+ /** length: 1..120 */
4414
+ name?: string;
4415
+ /** length: 0..500 */
4416
+ description?: string;
4417
+ reportType?: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics";
4418
+ /** Параметры отчёта, как в query живого отчёта. Вместо from/to можно задать relativePeriod {unit: day|week|month, offset} — даты вычислятся при запуске. В ответе лежат под именем `parametersJson` */
4419
+ parameters?: {
4420
+ [key: string]: unknown;
4421
+ };
4422
+ /** true — только admin */
4423
+ isShared?: boolean;
4424
+ }
4425
+ export interface ReportDrilldownBody {
4426
+ reportType?: string;
4427
+ /** @defaultValue {} */
4428
+ filters?: {
4429
+ [key: string]: unknown;
4430
+ };
4431
+ /** @defaultValue {} */
4432
+ drilldown?: {
4433
+ [key: string]: unknown;
4434
+ };
4435
+ snapshotId?: string | null;
4436
+ /**
4437
+ * range: 1..500
4438
+ * @defaultValue 100
4439
+ */
4440
+ limit?: number;
4441
+ cursor?: string | null;
3750
4442
  }
3751
4443
  export interface ReportExport {
3752
4444
  id: string;
4445
+ vaultId: string;
3753
4446
  snapshotId: string;
4447
+ /** json | csv */
3754
4448
  format: string;
4449
+ /** queued | processing | completed | failed; экспорт синхронный, поэтому сейчас всегда completed */
3755
4450
  status: string;
3756
- filename: string | null;
4451
+ requestedById: string;
4452
+ requestedAt: string;
4453
+ completedAt: string | null;
4454
+ /** Задел под R2; сейчас всегда null */
4455
+ storageKey: string | null;
3757
4456
  mimeType: string | null;
4457
+ filename: string | null;
4458
+ /** Длина содержимого в СИМВОЛАХ, а не в байтах: для не-ASCII текста (кириллица в подписях адресов) файл на диске будет больше */
3758
4459
  sizeBytes: number | null;
4460
+ /** sha256 содержимого файла */
3759
4461
  fileHash: string | null;
3760
- requestedById: string;
3761
- completedAt: string | null;
4462
+ errorCode: string | null;
4463
+ errorMessage: string | null;
3762
4464
  }
3763
- export interface ReportSnapshot {
4465
+ export interface ReportSnapshotDetail {
3764
4466
  id: string;
3765
- vaultId: string;
3766
4467
  /** snake_case, как и везде */
3767
4468
  reportType: string;
3768
- status: string;
3769
- schemaVersion: string | null;
4469
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
4470
+ schemaVersion: string;
4471
+ /** Тело `parameters` запроса создания, как есть */
3770
4472
  parametersJson: {
3771
4473
  [key: string]: unknown;
3772
- } | null;
3773
- /** Хеш нормализованных параметров — одинаковый запрос даёт одинаковый хеш */
3774
- normalizedParametersHash: string | null;
3775
- result?: {
3776
- [key: string]: unknown;
3777
- } | null;
3778
- /** canonical sha256 результата — доказательство, что содержимое не подменили */
3779
- resultHash: string | null;
3780
- /** Задел под R2; сейчас всегда null */
3781
- resultStorageKey: string | null;
3782
- rowCount: number | null;
3783
- /** Манифест: ключи операций, вошедших в снапшот (для drill-down по снапшоту) */
3784
- includedOperationKeysJson?: string[] | null;
3785
- includedOperationKeysStorageKey?: string | null;
3786
- dataCutoffAt: string | null;
4474
+ };
4475
+ generatedById: string;
4476
+ generatedAt: string;
4477
+ /** Срез данных: операции новее в снапшот не вошли. Совпадает с generatedAt */
4478
+ dataCutoffAt: string;
4479
+ /** Число операций в манифесте; 0, если манифеста нет — у отчётов без пооперационной выборки (balance_position, approval_activity) */
4480
+ rowCount: number;
4481
+ /** sha256 canonical JSON результата — доказательство, что содержимое не подменили */
4482
+ resultHash: string;
4483
+ /** generating | completed | failed; создание синхронное, поэтому сейчас всегда completed */
4484
+ status: string;
4485
+ createdAt: string;
4486
+ vaultId: string;
4487
+ /** sha256 canonical JSON параметров — одинаковый запрос даёт одинаковый хеш */
4488
+ normalizedParametersHash: string;
4489
+ /** Задел; сейчас всегда null */
3787
4490
  dataCutoffBlock: number | null;
4491
+ /** current | historical — из параметра counterpartyClassification, по умолчанию historical */
3788
4492
  counterpartyClassificationMode: string | null;
4493
+ /** current | historical — из параметра folderClassification, по умолчанию historical */
3789
4494
  folderClassificationMode: string | null;
4495
+ /** Задел под R2; сейчас всегда null */
4496
+ resultStorageKey: string | null;
4497
+ /** Манифест: ключи операций, вошедших в снапшот (по нему строится drill-down по снапшоту). null — у отчёта нет пооперационной выборки или манифест собрать не удалось */
4498
+ includedOperationKeysJson: string[] | null;
4499
+ /** Задел под R2; сейчас всегда null */
4500
+ includedOperationKeysStorageKey: string | null;
3790
4501
  errorCode: string | null;
3791
4502
  errorMessage: string | null;
4503
+ /** В ответах всегда null: скрытый (soft-delete) снапшот не отдаётся вовсе — 404 */
4504
+ deletedAt: string | null;
4505
+ /** Результат отчёта, зафиксированный при создании. В ответе создания то же самое называется `result`. null возможен только у снапшота, который не достроился (status ≠ completed) */
4506
+ resultJson: {
4507
+ [key: string]: unknown;
4508
+ } | null;
4509
+ }
4510
+ export interface ReportSnapshotListItem {
4511
+ id: string;
4512
+ /** snake_case, как и везде */
4513
+ reportType: string;
4514
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
4515
+ schemaVersion: string;
4516
+ /** Тело `parameters` запроса создания, как есть */
4517
+ parametersJson: {
4518
+ [key: string]: unknown;
4519
+ };
4520
+ generatedById: string;
4521
+ generatedAt: string;
4522
+ /** Срез данных: операции новее в снапшот не вошли. Совпадает с generatedAt */
4523
+ dataCutoffAt: string;
4524
+ /** Число операций в манифесте; 0, если манифеста нет — у отчётов без пооперационной выборки (balance_position, approval_activity) */
4525
+ rowCount: number;
4526
+ /** sha256 canonical JSON результата — доказательство, что содержимое не подменили */
4527
+ resultHash: string;
4528
+ /** generating | completed | failed; создание синхронное, поэтому сейчас всегда completed */
4529
+ status: string;
4530
+ createdAt: string;
4531
+ }
4532
+ export interface ReportSnapshotList {
4533
+ items: ReportSnapshotListItem[];
4534
+ }
4535
+ export interface ReportSnapshot {
4536
+ id: string;
4537
+ /** snake_case, как и везде */
4538
+ reportType: string;
4539
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
4540
+ schemaVersion: string;
4541
+ /** Тело `parameters` запроса создания, как есть */
4542
+ parametersJson: {
4543
+ [key: string]: unknown;
4544
+ };
3792
4545
  generatedById: string;
3793
4546
  generatedAt: string;
4547
+ /** Срез данных: операции новее в снапшот не вошли. Совпадает с generatedAt */
4548
+ dataCutoffAt: string;
4549
+ /** Число операций в манифесте; 0, если манифеста нет — у отчётов без пооперационной выборки (balance_position, approval_activity) */
4550
+ rowCount: number;
4551
+ /** sha256 canonical JSON результата — доказательство, что содержимое не подменили */
4552
+ resultHash: string;
4553
+ /** generating | completed | failed; создание синхронное, поэтому сейчас всегда completed */
4554
+ status: string;
3794
4555
  createdAt: string;
4556
+ vaultId: string;
4557
+ /** sha256 canonical JSON параметров — одинаковый запрос даёт одинаковый хеш */
4558
+ normalizedParametersHash: string;
4559
+ /** Задел; сейчас всегда null */
4560
+ dataCutoffBlock: number | null;
4561
+ /** current | historical — из параметра counterpartyClassification, по умолчанию historical */
4562
+ counterpartyClassificationMode: string | null;
4563
+ /** current | historical — из параметра folderClassification, по умолчанию historical */
4564
+ folderClassificationMode: string | null;
4565
+ /** Задел под R2; сейчас всегда null */
4566
+ resultStorageKey: string | null;
4567
+ /** Манифест: ключи операций, вошедших в снапшот (по нему строится drill-down по снапшоту). null — у отчёта нет пооперационной выборки или манифест собрать не удалось */
4568
+ includedOperationKeysJson: string[] | null;
4569
+ /** Задел под R2; сейчас всегда null */
4570
+ includedOperationKeysStorageKey: string | null;
4571
+ errorCode: string | null;
4572
+ errorMessage: string | null;
4573
+ /** В ответах всегда null: скрытый (soft-delete) снапшот не отдаётся вовсе — 404 */
3795
4574
  deletedAt: string | null;
4575
+ /** Результат отчёта — тот же объект, что вернул бы живой отчёт. ТОЛЬКО в ответе создания; деталь отдаёт его как `resultJson` */
4576
+ result: {
4577
+ [key: string]: unknown;
4578
+ };
3796
4579
  }
3797
4580
  export interface ReportTemplate {
3798
4581
  id: string;
3799
4582
  vaultId: string;
4583
+ /** Создатель шаблона */
4584
+ ownerId: string;
3800
4585
  name: string;
4586
+ description: string | null;
3801
4587
  reportType: string;
4588
+ /** Тело `parameters` запроса создания, как есть (включая relativePeriod) */
3802
4589
  parametersJson: {
3803
4590
  [key: string]: unknown;
3804
- } | null;
4591
+ };
3805
4592
  isShared: boolean;
3806
- createdById: string;
3807
4593
  createdAt: string;
3808
- updatedAt?: string;
4594
+ updatedAt: string;
3809
4595
  }
3810
4596
  //# sourceMappingURL=types.d.ts.map