@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,14 +1,14 @@
1
1
  // Generated from openapi/spec.json by scripts/generate.ts — do not edit by hand.
2
2
  // Policy Engine REST API 1.0.0. Regenerate: npm run spec:update && npm run generate
3
3
 
4
- /** Единая форма ошибки для ВСЕХ эндпоинтов. Приводится слоем middleware/errorEnvelope независимо от того, как ошибку собрал хендлер */
4
+ /** Единая форма ошибки. Приводится слоем middleware/errorEnvelope независимо от того, как ошибку собрал хендлер. Слой узнаёт ошибку по ключу `error` в корне тела; ответ со статусом ≥400 без этого ключа он не трогает — так отвечает пакетная выдача доступа, когда отклонены все получатели: 400 с телом `{results}` */
5
5
  export interface ApiErrorEnvelope {
6
6
  error: {
7
7
  /** Машиночитаемый код. Осмысленный там, где его задаёт хендлер (INSUFFICIENT_BALANCE, POLICY_DENIED, APPROVAL_UNSATISFIABLE…), иначе выведен из HTTP-статуса (NOT_FOUND, FORBIDDEN…) */
8
8
  code: string
9
9
  /** Человекочитаемое описание — можно показывать пользователю */
10
10
  message: string
11
- /** Структурированные подробности, если есть */
11
+ /** Структурированные подробности, если есть (поля валидации, трейс политик). Сюда же слой переносит машиночитаемые ключи, которые хендлер положил рядом с error: `field` — ключ поля категории, не прошедшего валидацию; `stepIndex` — номер отказавшего шага композитной заявки */
12
12
  details?: unknown
13
13
  }
14
14
  }
@@ -159,6 +159,8 @@ export interface AddressAccessGrant {
159
159
  canCreateAddress: boolean
160
160
  grantorId: string
161
161
  status: "active" | "revoked"
162
+ revokedAt: string | null
163
+ revokedById: string | null
162
164
  /** format: date-time */
163
165
  createdAt: string
164
166
  /** format: date-time */
@@ -248,8 +250,8 @@ export interface AddressDetail {
248
250
  tokenId: string | null
249
251
  /** format: date-time */
250
252
  createdAt: string
251
- /** Сводка совместного доступа. Приходит в списке и детали адреса; PATCH /addresses/{id} переиспользует эту же схему и sharing НЕ возвращает */
252
- sharing?: SharingSummary
253
+ /** Сводка совместного доступа. Приходит всегда: в списке, ответе PATCH и детали адреса */
254
+ sharing: SharingSummary
253
255
  /** Код токена адреса (из segmentsJson + asset route) */
254
256
  asset: string | null
255
257
  assetName: string | null
@@ -367,8 +369,17 @@ export interface AddressGroupDetail {
367
369
  createdAt: string
368
370
  /** format: date-time */
369
371
  updatedAt: string
370
- /** range: -9007199254740991..9007199254740991 */
372
+ /**
373
+ * Сколько адресов в папке. Приходит в списке и детали
374
+ *
375
+ * range: -9007199254740991..9007199254740991
376
+ */
371
377
  memberCount?: number
378
+ /** Служебный счётчик ORM — приезжает в списке и детали рядом с memberCount и равен ему. Читайте memberCount: это поле — деталь реализации, объявлено только потому, что фактически отдаётся */
379
+ _count?: {
380
+ /** range: -9007199254740991..9007199254740991 */
381
+ members: number
382
+ }
372
383
  /** Как у меня доступ к группе */
373
384
  access?: "manager" | "granted" | "admin"
374
385
  /** Открыта ли папка кому-то ещё + сколько ЛЮДЕЙ имеют доступ. Ортогонально полю access: своя расшаренная папка — это access=manager + isShared=true */
@@ -404,8 +415,17 @@ export interface AddressGroup {
404
415
  createdAt: string
405
416
  /** format: date-time */
406
417
  updatedAt: string
407
- /** range: -9007199254740991..9007199254740991 */
418
+ /**
419
+ * Сколько адресов в папке. Приходит в списке и детали
420
+ *
421
+ * range: -9007199254740991..9007199254740991
422
+ */
408
423
  memberCount?: number
424
+ /** Служебный счётчик ORM — приезжает в списке и детали рядом с memberCount и равен ему. Читайте memberCount: это поле — деталь реализации, объявлено только потому, что фактически отдаётся */
425
+ _count?: {
426
+ /** range: -9007199254740991..9007199254740991 */
427
+ members: number
428
+ }
409
429
  /** Как у меня доступ к группе */
410
430
  access?: "manager" | "granted" | "admin"
411
431
  /** Открыта ли папка кому-то ещё + сколько ЛЮДЕЙ имеют доступ. Ортогонально полю access: своя расшаренная папка — это access=manager + isShared=true */
@@ -471,8 +491,8 @@ export interface AddressListItem {
471
491
  assetSymbol: string | null
472
492
  /** Баланс в decimal (по token.decimals). NULL — ledger-счёта ещё нет */
473
493
  balanceDecimal: string | null
474
- /** Сводка совместного доступа. Приходит в списке и детали адреса; PATCH /addresses/{id} переиспользует эту же схему и sharing НЕ возвращает */
475
- sharing?: SharingSummary
494
+ /** Сводка совместного доступа. Приходит всегда: в списке, ответе PATCH и детали адреса */
495
+ sharing: SharingSummary
476
496
  /** Владелец адреса (создатель) с UI-метаданными */
477
497
  owner: MemberCard | null
478
498
  access: AddressAccess
@@ -771,11 +791,11 @@ export interface ApprovalRequestDetail {
771
791
 
772
792
  export interface ApprovalRequestEvent {
773
793
  id: string
774
- /** created|voted|approved|rejected|cancelled|applied|apply_failed|stale */
794
+ /** created|voted|approved|rejected|cancelled|applied|apply_failed|stale|folder_skipped. folder_skipped идёт ПОСЛЕ applied: адрес по заявке create_address создан, но в папку, выбранную при подаче, не положен (папку удалили или у инициатора отозвали право) — причина в comment */
775
795
  type: string
776
796
  actorId: string | null
777
797
  decision: string | null
778
- /** Пояснение ПОДПИСАНТА. У события applied всегда null — id созданного объекта лежит в resultId, чтобы клиент не напечатал его как комментарий */
798
+ /** У voted — пояснение ПОДПИСАНТА; у stale, apply_failed и folder_skipped — системное пояснение для человека. У события applied всегда null — id созданного объекта лежит в resultId, чтобы клиент не напечатал его как комментарий */
779
799
  comment: string | null
780
800
  /** Только у applied: id созданного/изменённого объекта */
781
801
  resultId: string | null
@@ -931,7 +951,7 @@ export interface AssignCategoryBody {
931
951
  * Значения полей; обязательные (required) должны быть заполнены
932
952
  * @defaultValue {}
933
953
  */
934
- values: {
954
+ values?: {
935
955
  [key: string]: unknown
936
956
  }
937
957
  /** length: 0..500 */
@@ -1001,7 +1021,12 @@ export interface CancelResult {
1001
1021
  /** Тело не требуется — инициатор из сессии */
1002
1022
  export type CancelTransferBody = Record<string, never>
1003
1023
 
1004
- export type CategoryAccessScope = unknown
1024
+ /**
1025
+ * Что категория открывает по умолчанию, без гранта (какое именно право — зависит от поля: viewScope, assignScope, editScope). owner_only — ничего сверх доступа через адрес операции (свои операции, совместный доступ) и грантов категории; vault_members — право есть у каждого участника Vault'а.
1026
+ *
1027
+ * В ответах приходят только эти два значения. В ЗАПРОСЕ дополнительно принимается устаревший псевдоним "restricted": записывается как "owner_only" (вёл себя так же; миграция 0037 убрала его из данных). В перечень значений он не входит намеренно: описание общее для запроса и ответа, а в ответе его не бывает. Новому коду слать его незачем
1028
+ */
1029
+ export type CategoryAccessScope = "owner_only" | "vault_members"
1005
1030
 
1006
1031
  export interface CompositeApplied {
1007
1032
  /** Все шаги allow/bypass — цепочка применена сразу, без заявки */
@@ -1303,7 +1328,7 @@ export interface CreateAddressBody {
1303
1328
  * length: 1..16
1304
1329
  */
1305
1330
  emoji?: string
1306
- /** Создать адрес В группу адресов (нужно право create_address на группе). Новый адрес — member группы, доступен её участникам */
1331
+ /** Создать адрес В группу адресов (нужно право create_address на группе). Новый адрес — member группы, доступен её участникам. Если создание ушло на подпись (202), папка сохраняется в заявке, а право на неё перепроверяется при применении: отозвали — адрес создаётся без папки, с оговоркой applyWarnings в ответе голосования. При прямом создании (201) то же — оговорка `warnings` в теле ответа */
1307
1332
  groupId?: string
1308
1333
  }
1309
1334
 
@@ -1329,6 +1354,15 @@ export interface CreateAddressResult {
1329
1354
  label: string | null
1330
1355
  description: string | null
1331
1356
  }
1357
+ /** Создано, но не всё: адрес есть, а в папку из `groupId` не положен. Право на папку роут проверяет до создания (иначе 403), а запись членства — уже после Wallet Service и мониторинга; право могли отозвать в этом окне или запись могла не пройти. Есть только когда непусто; не показывать адрес в папке, если оговорка пришла */
1358
+ warnings?: FolderSkipWarning[]
1359
+ }
1360
+
1361
+ export interface FolderSkipWarning {
1362
+ /** FOLDER_NOT_FOUND — папку удалили; FOLDER_FORBIDDEN — у создателя адреса больше нет права заводить в неё адреса; FOLDER_WRITE_FAILED — сбой записи */
1363
+ code: "FOLDER_NOT_FOUND" | "FOLDER_FORBIDDEN" | "FOLDER_WRITE_FAILED"
1364
+ /** Готовая фраза для человека */
1365
+ message: string
1332
1366
  }
1333
1367
 
1334
1368
  export interface CreateCompositeBody {
@@ -1838,6 +1872,8 @@ export type EvaluationResult = {
1838
1872
  export interface PolicyTrace {
1839
1873
  policyId: string
1840
1874
  policyCode: string
1875
+ /** Редакция политики, которая оценивалась (Ф4) — та же, что попадёт в `decisionPolicyVersionId` перевода. null — у политики ещё нет версий */
1876
+ policyVersionId: string | null
1841
1877
  policyName: string
1842
1878
  effect: string
1843
1879
  applied: boolean
@@ -2546,21 +2582,38 @@ export interface Participant {
2546
2582
  userId: string
2547
2583
  user: UserCard | null
2548
2584
  role: ShareRole
2549
- /**
2550
- * ОТКУДА доступ — и, следовательно, что делать, чтобы его убрать:
2551
- * - {kind:"direct", grantId} — прямой грант, отзывается точечно
2552
- * - {kind:"user_group", groupId, groupName} — грант выдан ГРУППЕ пользователей: точечно не отзывается, нужно вывести человека из группы
2553
- * - {kind:"address_group", groupId, groupName} — доступ через папку, куда входит адрес
2554
- *
2555
- * Источников может быть НЕСКОЛЬКО сразу; итоговая роль — максимум прав
2556
- */
2557
- via: Array<{
2558
- [key: string]: unknown
2559
- }>
2585
+ /** Источников может быть НЕСКОЛЬКО сразу; итоговая роль — максимум прав */
2586
+ via: ParticipantVia[]
2560
2587
  /** true — есть прямой грант, участника можно убрать одной операцией */
2561
2588
  removable: boolean
2562
2589
  }
2563
2590
 
2591
+ /**
2592
+ * ОТКУДА доступ — и, следовательно, что делать, чтобы его убрать:
2593
+ * - direct — прямой грант, отзывается точечно
2594
+ * - user_group — грант выдан ГРУППЕ пользователей: точечно не отзывается, нужно вывести человека из группы
2595
+ * - address_group — доступ через папку, куда входит адрес (у участников самой папки не встречается)
2596
+ */
2597
+ export type ParticipantVia = {
2598
+ kind: "direct"
2599
+ /** Грант на сам объект — отзывается точечно */
2600
+ grantId: string
2601
+ } | {
2602
+ kind: "user_group"
2603
+ /** Грант, выданный группе пользователей */
2604
+ grantId: string
2605
+ groupId: string
2606
+ /** Имя группы — чтобы объяснить «доступ у вас от группы Казначеи» */
2607
+ groupName: string
2608
+ } | {
2609
+ kind: "address_group"
2610
+ /** Грант, выданный на папку */
2611
+ grantId: string
2612
+ /** Папка, в которой лежит адрес */
2613
+ groupId: string
2614
+ groupName: string
2615
+ }
2616
+
2564
2617
  /** Кто имеет доступ к объекту — списком ЛЮДЕЙ, а не грантов. Админы не включаются: их доступ надзорный, есть ко всему сразу и совместным доступом не является */
2565
2618
  export interface ParticipantsResponse {
2566
2619
  /** Владелец объекта: у адреса — создатель, у папки — менеджер. Не участник */
@@ -2798,6 +2851,7 @@ export interface PendingApproval {
2798
2851
 
2799
2852
  export interface PolicyApprover {
2800
2853
  id: string
2854
+ policyId: string
2801
2855
  /** "user" | "group" */
2802
2856
  kind: string
2803
2857
  userId: string | null
@@ -2832,6 +2886,14 @@ export interface PolicyDetail {
2832
2886
  createdAt: string
2833
2887
  /** format: date-time */
2834
2888
  updatedAt: string
2889
+ /**
2890
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
2891
+ *
2892
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
2893
+ *
2894
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
2895
+ */
2896
+ currentVersionId: string | null
2835
2897
  /** LEGACY: движок их не читает, решает conditionJson */
2836
2898
  rules: Rule[]
2837
2899
  /** Явные одобряющие (для allow-политики с approval{}) */
@@ -2982,6 +3044,14 @@ export interface Policy {
2982
3044
  createdAt: string
2983
3045
  /** format: date-time */
2984
3046
  updatedAt: string
3047
+ /**
3048
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
3049
+ *
3050
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
3051
+ *
3052
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
3053
+ */
3054
+ currentVersionId: string | null
2985
3055
  }
2986
3056
 
2987
3057
  export interface PolicyTableItem {
@@ -3000,10 +3070,6 @@ export interface PolicyTableItem {
3000
3070
  adminBypass: boolean
3001
3071
  allowInitiatorApproval: boolean
3002
3072
  enabled: boolean
3003
- /** Условие применимости — булево дерево. ЭТО ЧИТАЕТ ДВИЖОК. null — политика применима всегда. Legacy-таблица rules движком не используется и удаляется в Ф5 */
3004
- conditionJson: PolicyCondition | null
3005
- /** Требование подписи. null — политика разрешает без одобрения */
3006
- approvalJson: PolicyApprovalInput | null
3007
3073
  /** format: date-time */
3008
3074
  createdAt: string
3009
3075
  /** format: date-time */
@@ -3065,6 +3131,14 @@ export interface PolicyWithRules {
3065
3131
  createdAt: string
3066
3132
  /** format: date-time */
3067
3133
  updatedAt: string
3134
+ /**
3135
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
3136
+ *
3137
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
3138
+ *
3139
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
3140
+ */
3141
+ currentVersionId: string | null
3068
3142
  /** LEGACY: движок их не читает, решает conditionJson */
3069
3143
  rules: Rule[]
3070
3144
  /** Явные одобряющие (для allow-политики с approval{}) */
@@ -3138,6 +3212,8 @@ export interface RequestVoteResult {
3138
3212
  resolvedResultId: string | null
3139
3213
  /** Только для композитных заявок: прогресс по шагам */
3140
3214
  stepsProgress?: StepProgress[]
3215
+ /** Применено, но не всё: адрес по заявке create_address создан (resolvedResultId), а в папку, выбранную при подаче, не положен. Право на папку проверяется при ПРИМЕНЕНИИ, а не при подаче. Есть только когда непусто; в истории заявки — событие folder_skipped */
3216
+ applyWarnings?: FolderSkipWarning[]
3141
3217
  }
3142
3218
 
3143
3219
  export interface StepProgress {
@@ -3184,21 +3260,45 @@ export interface SeatCreated {
3184
3260
  expiresAt: string
3185
3261
  }
3186
3262
 
3263
+ export interface ShareBatchAllRejected {
3264
+ /** items: 1..∞ */
3265
+ results: ShareBatchRejectedItem[]
3266
+ }
3267
+
3268
+ export interface ShareBatchRejectedItem {
3269
+ subjectType: "user" | "group"
3270
+ subjectUserId: string | null
3271
+ subjectGroupId: string | null
3272
+ ok: false
3273
+ outcome: null
3274
+ grant: null
3275
+ error: {
3276
+ /** SELF_SHARE — себе; OWNER_SUBJECT — владельцу адреса; NOT_MEMBER — не участник Vault'а; NOT_VISIBLE — получатель вам не виден (нет гранта видимости); GROUP_NOT_FOUND — группы нет в этом Vault'е */
3277
+ code: string
3278
+ /** Сейчас дублирует code — человеческий текст собирает клиент */
3279
+ message: string
3280
+ }
3281
+ }
3282
+
3283
+ export interface ShareBatchGrantedItem {
3284
+ subjectType: "user" | "group"
3285
+ subjectUserId: string | null
3286
+ subjectGroupId: string | null
3287
+ ok: true
3288
+ /** created — новый грант; updated — у действующего гранта сменили права; reactivated — снят отзыв с ранее отозванного */
3289
+ outcome: "created" | "updated" | "reactivated"
3290
+ grant: AddressAccessGrant
3291
+ error: null
3292
+ }
3293
+
3294
+ export type ShareBatchItem = ShareBatchGrantedItem | ShareBatchRejectedItem
3295
+
3187
3296
  export interface ShareBatchResult {
3188
- results: Array<{
3189
- subjectType: "user" | "group"
3190
- subjectUserId: string | null
3191
- subjectGroupId: string | null
3192
- ok: boolean
3193
- outcome: "created" | "updated" | "reactivated" | null
3194
- grant: AddressAccessGrant | null
3195
- error: {
3196
- code: string
3197
- message: string
3198
- } | null
3199
- }>
3297
+ results: ShareBatchItem[]
3200
3298
  }
3201
3299
 
3300
+ export type ShareGrantCreated = AddressAccessGrant | ShareBatchResult
3301
+
3202
3302
  export interface Token {
3203
3303
  /** cuid */
3204
3304
  id: string
@@ -3311,6 +3411,16 @@ export interface TransactionDetail {
3311
3411
  amlScore: number | null
3312
3412
  /** Когда провайдер сделал проверку */
3313
3413
  amlCheckedAt: string | null
3414
+ /** USD-оценка перевода: amountDecimal × usdRate, decimal-СТРОКА. Считается один раз при создании и live-курсом не пересчитывается; null — курс был недоступен */
3415
+ usdValue: string | null
3416
+ /** Редакция политики, принявшая решение (PolicyVersion.id, Ф4). ЕДИНСТВЕННЫЙ способ связать перевод с правилами, которые действовали в момент решения: `decisionPolicyId` ведёт на политику, а её с тех пор могли переписать. Снимок — GET /policies/{id}/versions. null — решение принято до введения версий */
3417
+ decisionPolicyVersionId: string | null
3418
+ /** Группы контрагентов получателя НА МОМЕНТ РЕШЕНИЯ (id групп). Версия политики хранит id групп белого списка, но не их состав — без этого снимка нельзя объяснить, почему платёж прошёл, после того как контрагента из группы убрали. null — получатель не был контрагентом */
3419
+ decisionCounterpartyGroupsJson: string[] | null
3420
+ /** Контрагент, сопоставленный по toAddress при создании. Дублирует `counterparty.id` — отдаётся как колонка перевода */
3421
+ counterpartyId: string | null
3422
+ /** Клиентский ключ попытки, с которым перевод создан (см. тело POST /transfers). null — ключ не передавали */
3423
+ idempotencyKey: string | null
3314
3424
  amountDecimal: string
3315
3425
  assetSymbol: string
3316
3426
  }
@@ -3408,7 +3518,7 @@ export interface UserProfile {
3408
3518
  color: string | null
3409
3519
  /** Emoji-иконка */
3410
3520
  emoji: string | null
3411
- /** true — аккаунт создан по пригласительной ссылке и настоящая почта ещё НЕ указана (в email лежит технический адрес). Клиент обязан попросить адрес: пока его нет, человеку некуда прислать magic-link, и при истечении сессии он потеряет доступ. Привязка — POST /me/email */
3521
+ /** true — аккаунт создан по пригласительной ссылке и настоящая почта ещё НЕ указана (в email лежит технический адрес). Клиент обязан попросить адрес: пока его нет, человеку некуда прислать magic-link, и при истечении сессии он потеряет доступ. Привязка — POST /me/profile/email. Приходит и в GET, и в ответе PATCH */
3412
3522
  needsEmail: boolean
3413
3523
  /** format: date-time */
3414
3524
  createdAt: string
@@ -3498,6 +3608,7 @@ export interface VaultPoliciesPage {
3498
3608
  items: Array<{
3499
3609
  /** cuid */
3500
3610
  id: string
3611
+ groupId: string
3501
3612
  code: string
3502
3613
  name: string
3503
3614
  description: string | null
@@ -3519,11 +3630,17 @@ export interface VaultPoliciesPage {
3519
3630
  createdAt: string
3520
3631
  /** format: date-time */
3521
3632
  updatedAt: string
3633
+ /**
3634
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
3635
+ *
3636
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
3637
+ *
3638
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
3639
+ */
3640
+ currentVersionId: string | null
3522
3641
  _count: {
3523
3642
  /** range: -9007199254740991..9007199254740991 */
3524
3643
  rules: number
3525
- /** range: -9007199254740991..9007199254740991 */
3526
- limits?: number
3527
3644
  }
3528
3645
  /** Owner-группа политики */
3529
3646
  group: GroupRef
@@ -3572,8 +3689,12 @@ export interface VisibilityGrant {
3572
3689
  } | null
3573
3690
  status: string
3574
3691
  grantorId: string
3692
+ revokedAt: string | null
3693
+ revokedById: string | null
3575
3694
  /** format: date-time */
3576
3695
  createdAt: string
3696
+ /** format: date-time */
3697
+ updatedAt: string
3577
3698
  }
3578
3699
 
3579
3700
  export interface VoteBody {
@@ -3649,36 +3770,111 @@ export type WebhookResult = {
3649
3770
  eventId: string
3650
3771
  }
3651
3772
 
3652
- export interface ActivityReport {
3773
+ export interface AccountStatementReport {
3653
3774
  report: ReportMeta
3654
- summary?: ReportSummary
3655
- byAsset?: ByAssetRow[]
3656
- byCounterparty?: ByCounterpartyRow[]
3657
- byOwnAddress?: ByOwnAddressRow[]
3658
- byStatus?: ByStatusRow[]
3659
- byCategory?: ByCategoryRow[]
3660
- byAddressOwner?: ByAddressOwnerRow[]
3661
- timeline?: TimelinePoint[]
3662
- approvals?: ReportApprovals
3663
- observations?: Observation[]
3664
- dataQuality?: DataQuality
3665
- comparison?: Comparison | null
3775
+ balancesByAsset: StatementTokenRow[]
3776
+ timeline: Array<{
3777
+ /** Начало интервала (как periodStart у остальных) */
3778
+ period: string
3779
+ /** Задел; сейчас всегда пустая строка */
3780
+ opening: string
3781
+ /** Приток ТЕКСТОМ для печати: "1.5 USDT; 20 TRX" — сумма с символом, активы через «; ». Числом не разбирать. Перечислен КАЖДЫЙ актив, по которому в интервале были операции, даже если притока по нему не было: тогда он приходит как "0 USDT", а не пропадает */
3782
+ incoming: string
3783
+ /** Отток, в том же текстовом виде и с теми же нулевыми позициями */
3784
+ outgoing: string
3785
+ /** Задел; сейчас всегда "0" */
3786
+ adjustments: string
3787
+ /** Задел; сейчас всегда пустая строка */
3788
+ closing: string
3789
+ }>
3790
+ dataQuality: DataQuality
3666
3791
  }
3667
3792
 
3668
3793
  export interface ReportMeta {
3669
- id?: string | null
3670
- /** snake_case: transaction_activity и т.д. */
3671
- type: string
3672
- from: string | null
3673
- to: string | null
3674
- timezone: string | null
3675
- dateBasis?: string | null
3676
- baseCurrency?: string | null
3677
- classificationMode?: string | null
3678
- dataCutoff?: string | null
3679
- schemaVersion?: string | number | null
3794
+ /** Задел под сохранённый отчёт. У живого отчёта всегда null */
3795
+ id: string | null
3796
+ /** Тип отчёта, snake_case — тот же, что в пути и в ?type= */
3797
+ type: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics"
3680
3798
  generatedAt: string
3681
- generatedById?: string | null
3799
+ /** Кто запросил отчёт */
3800
+ generatedById: string
3801
+ /** Начало периода (включительно), ISO-8601 UTC. У balance_position from = to = момент позиции */
3802
+ from: string
3803
+ /** Конец периода (НЕ включительно), ISO-8601 UTC */
3804
+ to: string
3805
+ /** IANA-зона, в которой нарезан timeline. У balance_position всегда UTC */
3806
+ timezone: string
3807
+ /** По какой дате операция отнесена к периоду. Если параметр не прислан: blockchainAt у counterparty_activity и counterparty_group_activity, createdAt у остальных */
3808
+ dateBasis: "createdAt" | "blockchainAt" | "confirmedAt"
3809
+ baseCurrency: "USD"
3810
+ /** Значение параметра counterpartyClassification (по умолчанию historical). У balance_position всегда current */
3811
+ classificationMode: "current" | "historical"
3812
+ dataCutoff: {
3813
+ /** Операции новее в отчёт не вошли */
3814
+ timestamp: string
3815
+ /** Задел; сейчас всегда null */
3816
+ blockNumber: number | null
3817
+ }
3818
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
3819
+ schemaVersion: string
3820
+ }
3821
+
3822
+ export interface StatementTokenRow {
3823
+ tokenId: string
3824
+ assetCode: string
3825
+ assetSymbol: string
3826
+ /** Network.slug */
3827
+ network: string
3828
+ openingAvailable: string
3829
+ openingReserved: string
3830
+ openingTotal: string
3831
+ incomingExternal: string
3832
+ outgoingExternal: string
3833
+ incomingInternal: string
3834
+ outgoingInternal: string
3835
+ /** Ручные корректировки ledger (kind=adjustment) за период */
3836
+ adjustments: string
3837
+ closingAvailable: string
3838
+ closingReserved: string
3839
+ closingTotal: string
3840
+ /** (opening + движения + adjustments) − closing из ledger. Ожидается "0"; ненулевое значение — операции и ledger разошлись, и оно же считается в dataQuality.ledgerReconciliationDifferences */
3841
+ reconciliationDifference: string
3842
+ }
3843
+
3844
+ export interface DataQuality {
3845
+ totalOperations: number
3846
+ unmatchedOperations: number
3847
+ retroactivelyMatchedOperations: number
3848
+ operationsWithoutUsdRate: number
3849
+ operationsWithoutSelectedDate: number
3850
+ operationsWithoutBlockchainTime: number
3851
+ operationsWithoutPaymentPurpose: number
3852
+ operationsWithoutBusinessReference: number
3853
+ uncategorizedOperations: number
3854
+ conflictingCounterpartyClassifications: number
3855
+ invalidOperationRelations: number
3856
+ missingLedgerEntries: number
3857
+ ledgerReconciliationDifferences: number
3858
+ }
3859
+
3860
+ /** УСТАРЕЛО: используйте схему конкретного отчёта (TransactionActivityReport, AssetMovementReport, …) или AnyReport. Имя сохранено для совместимости сгенерированных клиентов: это объединение девяти отчётов, кроме category_analytics */
3861
+ export type ActivityReport = TransactionActivityReport | CounterpartyActivityReport | CounterpartyGroupActivityReport | AssetMovementReport | AddressActivityReport | AccountStatementReport | OperationsExceptionsReport | ApprovalActivityReport | BalancePositionReport
3862
+
3863
+ export interface TransactionActivityReport {
3864
+ report: ReportMeta
3865
+ summary: ReportSummary
3866
+ byAsset: ByAssetRow[]
3867
+ byCounterparty: ByCounterpartyRow[]
3868
+ byOwnAddress: ByOwnAddressRow[]
3869
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
3870
+ byAddressOwner?: ByAddressOwnerRow[]
3871
+ byStatus: ByStatusRow[]
3872
+ byCategory: ByCategoryRow[]
3873
+ timeline: TimelinePoint[]
3874
+ approvals: ReportApprovals
3875
+ comparison: Comparison
3876
+ observations: Observation[]
3877
+ dataQuality: DataQuality
3682
3878
  }
3683
3879
 
3684
3880
  export interface ReportSummary {
@@ -3709,32 +3905,39 @@ export interface ReportSummary {
3709
3905
  minimumAmountByAsset: AssetAmount[]
3710
3906
  /** @defaultValue [] */
3711
3907
  maximumAmountByAsset: AssetAmount[]
3712
- confirmedCount?: number
3713
- pendingCount?: number
3714
- awaitingApprovalCount?: number
3715
- rejectedCount?: number
3716
- cancelledCount?: number
3717
- failedCount?: number
3718
- activeDays?: number | null
3719
- firstOperationAt?: string | null
3720
- lastOperationAt?: string | null
3908
+ confirmedCount: number
3909
+ pendingCount: number
3910
+ awaitingApprovalCount: number
3911
+ rejectedCount: number
3912
+ cancelledCount: number
3913
+ failedCount: number
3914
+ /** Число календарных дней (в timezone отчёта) с хотя бы одной операцией */
3915
+ activeDays: number
3916
+ /** createdAt самой ранней операции выборки; null — выборка пуста */
3917
+ firstOperationAt: string | null
3918
+ /** createdAt самой поздней операции выборки; null — выборка пуста */
3919
+ lastOperationAt: string | null
3721
3920
  }
3722
3921
 
3723
3922
  export interface AssetAmount {
3724
- tokenId: string | null
3725
- assetCode: string | null
3923
+ tokenId: string
3924
+ /** Token.code из реестра */
3925
+ assetCode: string
3726
3926
  assetSymbol: string
3727
- network: string | null
3728
- contractAddress?: string | null
3729
- /** raw smallest units, строкой */
3927
+ /** Network.slug */
3928
+ network: string
3929
+ /** DECIMAL-строка в единицах актива ("1.5"), НЕ raw smallest units; хвостовые нули убраны. Со знаком только у netFlow* ("-12.5" — отток больше притока), у остальных блоков неотрицательна */
3730
3930
  amount: string
3731
3931
  }
3732
3932
 
3733
3933
  export interface ByAssetRow {
3734
- tokenId: string | null
3735
- assetCode: string | null
3934
+ tokenId: string
3935
+ /** Token.code из реестра */
3936
+ assetCode: string
3736
3937
  assetSymbol: string
3737
- network: string | null
3938
+ /** Network.slug */
3939
+ network: string
3940
+ /** null у нативного актива сети */
3738
3941
  contractAddress: string | null
3739
3942
  incoming: string
3740
3943
  outgoing: string
@@ -3758,8 +3961,11 @@ export interface ByAssetRow {
3758
3961
  }
3759
3962
 
3760
3963
  export interface ByCounterpartyRow {
3964
+ /** null — операции без сопоставленного контрагента (одна строка на все) */
3761
3965
  counterpartyId: string | null
3966
+ /** Имя контрагента. У строки без контрагента (counterpartyId = null) сюда приходит английская заглушка "unmatched counterparty" — она не локализуется, поэтому различать такую строку надо по counterpartyId */
3762
3967
  name: string | null
3968
+ /** Свободная категория контрагента (exchange, client…); null — не задана */
3763
3969
  kind: string | null
3764
3970
  /** @defaultValue [] */
3765
3971
  incomingByAsset: AssetAmount[]
@@ -3781,7 +3987,7 @@ export interface ByOwnAddressRow {
3781
3987
  [key: string]: unknown
3782
3988
  } | null
3783
3989
  /**
3784
- * Папки, в которых адрес состоял (зависит от folderClassification)
3990
+ * Задел под папки адреса. Сейчас ВСЕГДА пустой массив
3785
3991
  * @defaultValue []
3786
3992
  */
3787
3993
  folderSnapshots: unknown[]
@@ -3794,6 +4000,20 @@ export interface ByOwnAddressRow {
3794
4000
  operationsCount: number
3795
4001
  }
3796
4002
 
4003
+ export interface ByAddressOwnerRow {
4004
+ /** Владелец адресов (создатель) */
4005
+ userId: string
4006
+ /** Считается от вызывающего: у двух людей один отчёт разложится по-разному */
4007
+ isMe: boolean
4008
+ /** Сколько адресов этого владельца попало в выборку */
4009
+ addressesCount: number
4010
+ incomingByAsset: AssetAmount[]
4011
+ outgoingByAsset: AssetAmount[]
4012
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4013
+ grossTurnoverUsd: string | null
4014
+ operationsCount: number
4015
+ }
4016
+
3797
4017
  export interface ByStatusRow {
3798
4018
  status: string
3799
4019
  count: number
@@ -3814,17 +4034,6 @@ export interface ByCategoryRow {
3814
4034
  usdValue: string | null
3815
4035
  }
3816
4036
 
3817
- export interface ByAddressOwnerRow {
3818
- ownerId: string | null
3819
- owner?: {
3820
- [key: string]: unknown
3821
- } | null
3822
- isMe: boolean
3823
- operationsCount: number
3824
- /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3825
- grossTurnoverUsd: string | null
3826
- }
3827
-
3828
4037
  export interface TimelinePoint {
3829
4038
  periodStart: string
3830
4039
  periodEnd: string
@@ -3854,75 +4063,456 @@ export interface ReportApprovals {
3854
4063
  maximumApprovalSeconds: number | null
3855
4064
  }
3856
4065
 
4066
+ export interface Comparison {
4067
+ /** none | previous_period | previous_year */
4068
+ mode: string
4069
+ previousFrom: string | null
4070
+ previousTo: string | null
4071
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
4072
+ incomingUsdChangePercent: string | null
4073
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
4074
+ outgoingUsdChangePercent: string | null
4075
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
4076
+ grossTurnoverUsdChangePercent: string | null
4077
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
4078
+ operationsCountChangePercent: string | null
4079
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
4080
+ averageOperationUsdChangePercent: string | null
4081
+ }
4082
+
3857
4083
  export interface Observation {
4084
+ /** Машиночитаемый код: COUNTERPARTY_TURNOVER_INCREASED, SINGLE_OPERATION_CONCENTRATION, UNMATCHED_OPERATIONS_PRESENT… */
3858
4085
  code: string
3859
- severity: string
4086
+ severity: "info" | "warning"
3860
4087
  title: string
3861
4088
  description: string
3862
4089
  metric: string | null
3863
4090
  currentValue: string | null
4091
+ /** Задел; сейчас всегда null */
3864
4092
  comparisonValue: string | null
3865
- changePercent: number | null
3866
- drilldownFilters?: {
4093
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — наблюдение не про сравнение периодов (концентрация, качество данных, зависшие операции). Не null только у наблюдений о росте или падении оборота и среднего платежа — там это то же значение, что в comparison */
4094
+ changePercent: string | null
4095
+ /** Готовое уточнение для drill-down, раскрывающее наблюдение до операций; {} — раскрывать нечем */
4096
+ drilldownFilters: {
3867
4097
  [key: string]: unknown
3868
- } | null
4098
+ }
3869
4099
  }
3870
4100
 
3871
- export interface DataQuality {
3872
- totalOperations: number
3873
- unmatchedOperations: number
3874
- retroactivelyMatchedOperations: number
3875
- operationsWithoutUsdRate: number
3876
- operationsWithoutSelectedDate: number
3877
- operationsWithoutBlockchainTime: number
3878
- operationsWithoutPaymentPurpose: number
3879
- operationsWithoutBusinessReference: number
3880
- uncategorizedOperations: number
3881
- conflictingCounterpartyClassifications: number
3882
- invalidOperationRelations: number
3883
- missingLedgerEntries: number
3884
- ledgerReconciliationDifferences: number
4101
+ export interface CounterpartyActivityReport {
4102
+ report: ReportMeta
4103
+ /** Карточка контрагента на момент построения отчёта */
4104
+ counterparty: {
4105
+ id: string
4106
+ name: string
4107
+ kind: string | null
4108
+ /** Архивный контрагент отчёт не блокирует — история по нему остаётся */
4109
+ archived: boolean
4110
+ /** Адресов в справочнике у контрагента */
4111
+ addressesCount: number
4112
+ /** Группы контрагентов, в которых он состоит СЕЙЧАС */
4113
+ groupIds: string[]
4114
+ }
4115
+ summary: ReportSummary
4116
+ byAsset: ByAssetRow[]
4117
+ byCounterpartyAddress: ByCounterpartyAddressRow[]
4118
+ byOwnAddress: ByOwnAddressRow[]
4119
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
4120
+ byAddressOwner?: ByAddressOwnerRow[]
4121
+ byStatus: ByStatusRow[]
4122
+ byCategory: ByCategoryRow[]
4123
+ timeline: TimelinePoint[]
4124
+ approvals: ReportApprovals
4125
+ comparison: Comparison
4126
+ observations: Observation[]
4127
+ dataQuality: DataQuality
4128
+ }
4129
+
4130
+ export interface ByCounterpartyAddressRow {
4131
+ /** Запись справочника адресов контрагента; null — адрес в справочник не внесён */
4132
+ counterpartyAddressId: string | null
4133
+ /** Внешний адрес (on-chain) */
4134
+ address: string
4135
+ /** Network.slug */
4136
+ network: string
4137
+ /** Задел; сейчас всегда null */
4138
+ label: string | null
4139
+ incomingByAsset: AssetAmount[]
4140
+ outgoingByAsset: AssetAmount[]
4141
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4142
+ grossTurnoverUsd: string | null
4143
+ operationsCount: number
4144
+ incomingCount: number
4145
+ outgoingCount: number
4146
+ /** Доля адреса в USD-обороте отчёта, проценты decimal-строкой; null — нет USD-оценки или оборот нулевой */
4147
+ turnoverShareUsd: string | null
4148
+ firstOperationAt: string | null
4149
+ lastOperationAt: string | null
4150
+ /** true — до начала периода операций с этим адресом не было (в любом статусе, включая внутренние потоки) */
4151
+ isNewInPeriod: boolean
3885
4152
  }
3886
4153
 
3887
- export interface Comparison {
3888
- mode: string
3889
- previousFrom: string | null
3890
- previousTo: string | null
3891
- incomingUsdChangePercent: number | null
3892
- outgoingUsdChangePercent: number | null
3893
- grossTurnoverUsdChangePercent: number | null
3894
- operationsCountChangePercent: number | null
3895
- averageOperationUsdChangePercent: number | null
4154
+ export interface CounterpartyGroupActivityReport {
4155
+ report: ReportMeta
4156
+ group: {
4157
+ id: string
4158
+ code: string
4159
+ name: string
4160
+ /** Сколько контрагентов в группе СЕЙЧАС. При counterpartyClassification=historical выборка идёт по составу на момент отчёта и может не совпасть с этим числом */
4161
+ counterpartiesCount: number
4162
+ }
4163
+ summary: ReportSummary
4164
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
4165
+ byAddressOwner?: ByAddressOwnerRow[]
4166
+ byCounterparty: ByGroupCounterpartyRow[]
4167
+ byAsset: ByAssetRow[]
4168
+ byStatus: ByStatusRow[]
4169
+ byCategory: ByCategoryRow[]
4170
+ timeline: TimelinePoint[]
4171
+ approvals: ReportApprovals
4172
+ comparison: Comparison
4173
+ observations: Observation[]
4174
+ dataQuality: DataQuality
4175
+ }
4176
+
4177
+ export interface ByGroupCounterpartyRow {
4178
+ counterpartyId: string | null
4179
+ name: string | null
4180
+ kind: string | null
4181
+ incomingByAsset: AssetAmount[]
4182
+ outgoingByAsset: AssetAmount[]
4183
+ /** Приток минус отток по активу, со знаком */
4184
+ netFlowByAsset: AssetAmount[]
4185
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4186
+ grossTurnoverUsd: string | null
4187
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4188
+ netFlowUsd: string | null
4189
+ operationsCount: number
4190
+ /** Доля контрагента в USD-обороте группы, проценты decimal-строкой ("37.5"); null — у оборота нет USD-оценки или он нулевой */
4191
+ shareOfGroupTurnoverUsd: string | null
4192
+ firstOperationAt: string | null
4193
+ lastOperationAt: string | null
3896
4194
  }
3897
4195
 
3898
- export interface BalancesAsOf {
3899
- /** Момент среза (echo параметра ?at=) */
4196
+ export interface AssetMovementReport {
4197
+ report: ReportMeta
4198
+ asset: {
4199
+ tokenId: string
4200
+ /** Token.code из реестра */
4201
+ code: string
4202
+ symbol: string
4203
+ name: string
4204
+ decimals: number
4205
+ /** Network.slug */
4206
+ network: string
4207
+ /** null у нативного актива сети */
4208
+ contractAddress: string | null
4209
+ }
4210
+ /** Остаток актива на начало периода (from), из ledger. DECIMAL в единицах актива — не raw, в отличие от openingBalances у address_activity */
4211
+ openingBalance: {
4212
+ /** decimal */
4213
+ available: string
4214
+ /** decimal */
4215
+ reserved: string
4216
+ /** available + reserved, decimal */
4217
+ total: string
4218
+ }
4219
+ /** Остаток актива на конец периода (to), из ledger. DECIMAL в единицах актива — не raw, в отличие от openingBalances у address_activity */
4220
+ closingBalance: {
4221
+ /** decimal */
4222
+ available: string
4223
+ /** decimal */
4224
+ reserved: string
4225
+ /** available + reserved, decimal */
4226
+ total: string
4227
+ }
4228
+ summary: ReportSummary
4229
+ byCounterparty: ByCounterpartyBriefRow[]
4230
+ byOwnAddress: ByOwnAddressRow[]
4231
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
4232
+ byAddressOwner?: ByAddressOwnerRow[]
4233
+ byStatus: ByStatusRow[]
4234
+ byCategory: ByCategoryRow[]
4235
+ timeline: TimelinePoint[]
4236
+ comparison: Comparison
4237
+ observations: Observation[]
4238
+ dataQuality: DataQuality
4239
+ }
4240
+
4241
+ export interface ByCounterpartyBriefRow {
4242
+ /** null — операции без сопоставленного контрагента (одна строка на все) */
4243
+ counterpartyId: string | null
4244
+ /** Имя контрагента. У строки без контрагента (counterpartyId = null) сюда приходит английская заглушка "unmatched counterparty" — она не локализуется, поэтому различать такую строку надо по counterpartyId */
4245
+ name: string | null
4246
+ incomingByAsset: AssetAmount[]
4247
+ outgoingByAsset: AssetAmount[]
4248
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4249
+ grossTurnoverUsd: string | null
4250
+ operationsCount: number
4251
+ }
4252
+
4253
+ export interface AddressActivityReport {
4254
+ report: ReportMeta
4255
+ /** Цель отчёта, и форма у неё ДВЕ: при addressId — карточка адреса, при folderId — {folder:{id,name}}. Имя ключа одно на оба случая исторически */
4256
+ address: {
4257
+ id: string
4258
+ address: string
4259
+ label: string | null
4260
+ description: string | null
4261
+ /** Без апострофов: m/44/195/… */
4262
+ derivationPath: string
4263
+ segmentsJson: {
4264
+ [key: string]: unknown
4265
+ }
4266
+ /** Network.slug */
4267
+ network: string
4268
+ createdAt: string
4269
+ } | {
4270
+ folder: {
4271
+ id: string
4272
+ name: string
4273
+ }
4274
+ }
4275
+ /** Папки адреса. Вторая дата зависит от folderClassification: historical (по умолчанию) — validFrom из истории членства, current — createdAt текущего членства. При folderId — всегда пустой массив */
4276
+ folderMemberships: Array<{
4277
+ folderId: string
4278
+ validFrom: string
4279
+ } | {
4280
+ folderId: string
4281
+ createdAt: string
4282
+ }>
4283
+ /** Балансы счетов на начало периода (from), RAW. При addressId — счета этого адреса. При folderId выборка НЕ сужается до папки: приходят все счета scope вызывающего (у admin — весь Vault, включая external и adjustment) */
4284
+ openingBalances: ReportAccountBalance[]
4285
+ /** Балансы счетов на конец периода (to), RAW. При addressId — счета этого адреса. При folderId выборка НЕ сужается до папки: приходят все счета scope вызывающего (у admin — весь Vault, включая external и adjustment) */
4286
+ closingBalances: ReportAccountBalance[]
4287
+ summary: ReportSummary
4288
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
4289
+ byAddressOwner?: ByAddressOwnerRow[]
4290
+ /** Доля адреса (папки) в USD-обороте всего scope за тот же период, проценты decimal-строкой; null — нет USD-оценки или оборот scope нулевой */
4291
+ shareOfUserActivityUsd: string | null
4292
+ byAsset: ByAssetRow[]
4293
+ byCounterparty: ByCounterpartyBriefRow[]
4294
+ byStatus: ByStatusRow[]
4295
+ byCategory: ByCategoryRow[]
4296
+ timeline: TimelinePoint[]
4297
+ observations: Observation[]
4298
+ dataQuality: DataQuality
4299
+ }
4300
+
4301
+ export interface ReportAccountBalance {
4302
+ accountId: string
4303
+ /** available / reserved — счета адресов; external / adjustment — счета уровня Vault'а (без адреса): встречная сторона двойной записи */
4304
+ purpose: "available" | "reserved" | "external" | "adjustment"
4305
+ tokenId: string
4306
+ /** null у счетов уровня Vault'а */
4307
+ addressId: string | null
4308
+ /** Raw smallest units со знаком — НЕ decimal */
4309
+ balanceRaw: string
4310
+ }
4311
+
4312
+ export interface OperationsExceptionsReport {
4313
+ report: ReportMeta
4314
+ summary: {
4315
+ operationsCount: number
4316
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4317
+ usdValue: string | null
4318
+ /** null — выборка пуста */
4319
+ oldestOperationAt: string | null
4320
+ /** Возраст считается от createdAt до момента построения отчёта */
4321
+ averageAgeSeconds: number | null
4322
+ medianAgeSeconds: number | null
4323
+ }
4324
+ /** Всегда шесть строк в этом порядке, включая пустые корзины */
4325
+ ageBuckets: Array<{
4326
+ bucket: "0-15m" | "15-60m" | "1-6h" | "6-24h" | "1-3d" | ">3d"
4327
+ count: number
4328
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4329
+ usdValue: string | null
4330
+ }>
4331
+ byStatus: ByStatusRow[]
4332
+ byAsset: ByAssetRow[]
4333
+ byCounterparty: Array<{
4334
+ /** null — операции без сопоставленного контрагента (одна строка на все) */
4335
+ counterpartyId: string | null
4336
+ /** Имя контрагента. У строки без контрагента (counterpartyId = null) сюда приходит английская заглушка "unmatched counterparty" — она не локализуется, поэтому различать такую строку надо по counterpartyId */
4337
+ name: string | null
4338
+ count: number
4339
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4340
+ usdValue: string | null
4341
+ }>
4342
+ byInitiator: Array<{
4343
+ /** null — у операции нет инициатора (депозиты) */
4344
+ initiatorId: string | null
4345
+ count: number
4346
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4347
+ usdValue: string | null
4348
+ }>
4349
+ byDecisionPolicy: Array<{
4350
+ /** null — решение по операции политика не принимала */
4351
+ decisionPolicyId: string | null
4352
+ code: string | null
4353
+ count: number
4354
+ }>
4355
+ dataQuality: DataQuality
4356
+ }
4357
+
4358
+ export interface ApprovalActivityReport {
4359
+ report: ReportMeta
4360
+ summary: {
4361
+ requestsCount: number
4362
+ pendingCount: number
4363
+ approvedCount: number
4364
+ rejectedCount: number
4365
+ cancelledCount: number
4366
+ /** approved / (approved + rejected) × 100, decimal-строка; null — решённых заявок нет. Отменённые в знаменатель не входят */
4367
+ approvalRatePercent: string | null
4368
+ rejectionRatePercent: string | null
4369
+ /** cancelled / все заявки × 100; null — заявок нет */
4370
+ cancellationRatePercent: string | null
4371
+ /** От создания заявки до resolvedAt; null — решённых нет */
4372
+ averageResolutionSeconds: number | null
4373
+ medianResolutionSeconds: number | null
4374
+ maximumResolutionSeconds: number | null
4375
+ /** От создания заявки до первого голоса; null — голосов нет */
4376
+ averageFirstVoteSeconds: number | null
4377
+ medianFirstVoteSeconds: number | null
4378
+ requestsWithoutVotes: number
4379
+ }
4380
+ byAction: Array<{
4381
+ /** ApprovalRequest.action: transfer, composite… */
4382
+ action: string
4383
+ count: number
4384
+ approvedCount: number
4385
+ rejectedCount: number
4386
+ cancelledCount: number
4387
+ pendingCount: number
4388
+ averageResolutionSeconds: number | null
4389
+ }>
4390
+ byInitiator: Array<{
4391
+ userId: string
4392
+ name: string
4393
+ requestsCount: number
4394
+ approvedCount: number
4395
+ rejectedCount: number
4396
+ cancelledCount: number
4397
+ averageResolutionSeconds: number | null
4398
+ }>
4399
+ byApprover: Array<{
4400
+ userId: string
4401
+ /** Имя; если пользователь не найден — его userId */
4402
+ name: string
4403
+ assignedRequestsCount: number
4404
+ votedCount: number
4405
+ approveVotes: number
4406
+ rejectVotes: number
4407
+ pendingVotes: number
4408
+ averageVoteSeconds: number | null
4409
+ medianVoteSeconds: number | null
4410
+ }>
4411
+ byDecisionPolicy: Array<{
4412
+ decisionPolicyId: string | null
4413
+ code: string | null
4414
+ /** Задел; сейчас всегда null */
4415
+ name: string | null
4416
+ triggeredCount: number
4417
+ approvedCount: number
4418
+ rejectedCount: number
4419
+ pendingCount: number
4420
+ averageResolutionSeconds: number | null
4421
+ }>
4422
+ timeline: Array<{
4423
+ periodStart: string
4424
+ createdCount: number
4425
+ /** approved + rejected; отменённые не входят */
4426
+ resolvedCount: number
4427
+ approvedCount: number
4428
+ rejectedCount: number
4429
+ cancelledCount: number
4430
+ }>
4431
+ /** Только эхо параметра comparison: процентов изменения этот отчёт НЕ считает. У живого отчёта previous_year приходит как none; в снапшоте и при запуске шаблона значение не переводится и приходит как прислано */
4432
+ comparison: {
4433
+ mode: "none" | "previous_period" | "previous_year"
4434
+ }
4435
+ dataQuality: DataQuality
4436
+ }
4437
+
4438
+ export interface BalancePositionReport {
4439
+ report: ReportMeta
4440
+ /** Момент позиции, ISO-8601 UTC */
3900
4441
  asOf: string
3901
- /** @defaultValue [] */
3902
- totals: Array<{
3903
- [key: string]: unknown
4442
+ totalsByAsset: Array<{
4443
+ tokenId: string
4444
+ assetCode: string
4445
+ assetSymbol: string
4446
+ /** Network.slug */
4447
+ network: string
4448
+ /** decimal */
4449
+ available: string
4450
+ /** decimal */
4451
+ reserved: string
4452
+ /** available + reserved, decimal */
4453
+ total: string
4454
+ usdRate: string | null
4455
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4456
+ usdValue: string | null
3904
4457
  }>
3905
- /** @defaultValue [] */
3906
- accounts: Array<{
3907
- [key: string]: unknown
4458
+ byAddress: Array<{
4459
+ addressId: string
4460
+ address: string
4461
+ label: string | null
4462
+ balances: Array<{
4463
+ tokenId: string
4464
+ /** decimal */
4465
+ available: string
4466
+ /** decimal */
4467
+ reserved: string
4468
+ /** decimal */
4469
+ total: string
4470
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4471
+ usdValue: string | null
4472
+ }>
3908
4473
  }>
4474
+ concentration: {
4475
+ /** Доля крупнейшего адреса в USD, проценты decimal-строкой; null без USD-оценки */
4476
+ largestAddressShareUsd: string | null
4477
+ /** Доля пяти крупнейших адресов, проценты decimal-строкой; null без USD-оценки */
4478
+ top5AddressesShareUsd: string | null
4479
+ }
4480
+ dataQuality: DataQuality
3909
4481
  }
3910
4482
 
4483
+ /** Один из десяти отчётов. Форму определяет report.type: у каждого типа своя схема, общих для всех блоков — только report и (кроме category_analytics) dataQuality */
4484
+ export type AnyReport = TransactionActivityReport | CounterpartyActivityReport | CounterpartyGroupActivityReport | AssetMovementReport | AddressActivityReport | AccountStatementReport | OperationsExceptionsReport | ApprovalActivityReport | BalancePositionReport | CategoryAnalytics
4485
+
3911
4486
  export interface CategoryAnalytics {
3912
- report: ReportMeta
4487
+ report: {
4488
+ /** Тип отчёта, snake_case — тот же, что в пути и в ?type= */
4489
+ type: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics"
4490
+ generatedAt: string
4491
+ /** Начало периода (включительно), ISO-8601 UTC. У balance_position from = to = момент позиции */
4492
+ from: string
4493
+ /** Конец периода (НЕ включительно), ISO-8601 UTC */
4494
+ to: string
4495
+ /** IANA-зона, в которой нарезан timeline. У balance_position всегда UTC */
4496
+ timezone: string
4497
+ }
3913
4498
  categoryKey: string
4499
+ /** Операции с активным назначением этой категории */
3914
4500
  operationsCount: number
4501
+ /** null — metricField не задан */
3915
4502
  metric: {
3916
4503
  fieldKey: string
3917
- agg: string
4504
+ agg: "sum" | "avg" | "min" | "max" | "median" | "count"
3918
4505
  /** null, когда метрика деноминирована — см. byCurrency */
3919
4506
  value: string | null
3920
4507
  byCurrency: {
3921
4508
  [key: string]: unknown
3922
4509
  } | null
3923
4510
  } | null
4511
+ /** null — groupByField не задан */
3924
4512
  groups: Array<{
4513
+ /** Значение поля groupByField; null — у назначения поле не заполнено */
3925
4514
  key: string | null
4515
+ /** Число НАЗНАЧЕНИЙ категории в группе, а не операций: у одной операции их может быть несколько, поэтому сумма по группам бывает больше operationsCount отчёта */
3926
4516
  operationsCount: number
3927
4517
  metric: string | null
3928
4518
  metricByCurrency: {
@@ -3931,6 +4521,80 @@ export interface CategoryAnalytics {
3931
4521
  }> | null
3932
4522
  }
3933
4523
 
4524
+ export interface BalancesAsOfAccount {
4525
+ accountId: string
4526
+ /** available / reserved — счета адресов; external / adjustment — счета уровня Vault'а (без адреса): встречная сторона двойной записи */
4527
+ purpose: "available" | "reserved" | "external" | "adjustment"
4528
+ tokenId: string
4529
+ /** null у vault-level счетов */
4530
+ addressId: string | null
4531
+ /** Raw smallest units со знаком (vault-level счета бывают отрицательными) */
4532
+ balance: string
4533
+ /** То же в единицах актива. Если токена счёта нет в реестре — raw, как в balance */
4534
+ balanceDecimal: string
4535
+ }
4536
+
4537
+ export interface BalancesAsOf {
4538
+ /** Момент среза: параметр ?at=, приведённый к ISO-8601 в UTC (не буквальное эхо) */
4539
+ asOf: string
4540
+ /** По строке на токен; складываются только available и reserved. Пустой массив — движений до момента среза не было (или ?purpose= выбрал external/adjustment): это «нет данных», а не «ноль» */
4541
+ totals: BalancesAsOfTotal[]
4542
+ /** Счета, по которым были движения до момента среза. ОТСУТСТВУЕТ при ?summary=true. Участник видит только счета адресов из своего scope; vault-level счета (external/adjustment) — только admin */
4543
+ accounts?: BalancesAsOfAccount[]
4544
+ }
4545
+
4546
+ export interface BalancesAsOfTotal {
4547
+ token: {
4548
+ code: string
4549
+ symbol: string
4550
+ decimals: number
4551
+ /** Network.slug */
4552
+ network: string
4553
+ }
4554
+ tokenId: string
4555
+ /** DECIMAL (не raw!) — дубль availableDecimal; у GET /balances это поле raw */
4556
+ available: string
4557
+ availableDecimal: string
4558
+ /** DECIMAL (не raw!) — дубль reservedDecimal; у GET /balances это поле raw */
4559
+ reserved: string
4560
+ reservedDecimal: string
4561
+ /** available + reserved, decimal: резерв — ещё не расход, пока перевод не подтверждён */
4562
+ ownedTotal: string
4563
+ }
4564
+
4565
+ export interface CreateReportExportBody {
4566
+ /** json — canonical JSON результата; csv — плоский реестр операций. xlsx и pdf схема принимает, а хендлер отклоняет: 400 REPORT_UNSUPPORTED_FORMAT */
4567
+ format: "json" | "csv" | "xlsx" | "pdf"
4568
+ /** Зарезервировано: сейчас не влияет на файл */
4569
+ locale?: string
4570
+ /** Зарезервировано: сейчас не влияет на файл */
4571
+ timezone?: string
4572
+ /** Зарезервировано: сейчас не влияет на файл (csv — всегда реестр, json — всегда результат) */
4573
+ includeOperationRegister?: boolean
4574
+ }
4575
+
4576
+ export interface CreateReportSnapshotBody {
4577
+ reportType: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics"
4578
+ /** Те же параметры, что уходили в живой отчёт query-строкой (from, to, timezone, фильтры, includeShared, цель отчёта). Сохраняются в снапшоте как есть — под именем `parametersJson` */
4579
+ parameters: {
4580
+ [key: string]: unknown
4581
+ }
4582
+ }
4583
+
4584
+ export interface CreateReportTemplateBody {
4585
+ /** length: 1..120 */
4586
+ name: string
4587
+ /** length: 0..500 */
4588
+ description?: string
4589
+ reportType: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics"
4590
+ /** Параметры отчёта, как в query живого отчёта. Вместо from/to можно задать relativePeriod {unit: day|week|month, offset} — даты вычислятся при запуске. В ответе лежат под именем `parametersJson` */
4591
+ parameters: {
4592
+ [key: string]: unknown
4593
+ }
4594
+ /** true — только admin */
4595
+ isShared?: boolean
4596
+ }
4597
+
3934
4598
  export interface DrilldownResponse {
3935
4599
  /** Операции с полями и categoryAssignments (sensitive-значения отредактированы по правам: значение null + ключ в redactedFields) */
3936
4600
  items: Array<{
@@ -3946,94 +4610,251 @@ export interface DrilldownResponse {
3946
4610
  }
3947
4611
  }
3948
4612
 
4613
+ export interface LedgerEntriesPage {
4614
+ items: LedgerEntry[]
4615
+ nextCursor: string | null
4616
+ }
4617
+
3949
4618
  export interface LedgerEntry {
3950
4619
  id: string
3951
4620
  vaultId: string
3952
- /** deposit | transfer | adjustment … */
4621
+ /** topup | transfer_reserve | transfer_confirm | transfer_refund | adjustment | genesis. Это НЕ хранимый kind: три вида переименованы на выходе (reserve → transfer_reserve, confirm → transfer_confirm, release → transfer_refund), остальные отдаются как есть. Фильтр ?entryType= понимает оба написания */
3953
4622
  entryType: string
3954
- reference: string | null
3955
- description: string | null
3956
- operationType: string | null
4623
+ /** Документ-источник; null у корректировок и genesis */
4624
+ operationType: "transfer" | "deposit" | null
4625
+ /** id перевода или депозита — по operationType */
3957
4626
  operationId: string | null
3958
- token: {
3959
- [key: string]: unknown
3960
- } | null
3961
- effectiveAt: string
4627
+ /** Время записи в журнал. По нему идут сортировка, фильтр from/to и балансы as-of */
3962
4628
  postedAt: string
4629
+ /** Экономическое время события (для on-chain — время блока) */
4630
+ effectiveAt: string
4631
+ /** Ключ идемпотентности проводки (sourceKey), уникален: "topup:{eventId}", "reserve:{transferId}", "confirm:{transferId}", "release:{transferId}" */
4632
+ reference: string
4633
+ /** Примечание к проводке (memo) */
4634
+ description: string | null
4635
+ /** Токен проводки — по её ПЕРВОМУ постингу, взятому до фильтрации под права: присутствует, даже если postings у вызывающего пуст */
4636
+ token: LedgerEntryToken | null
4637
+ postings: LedgerPosting[]
4638
+ /** То же значение, что postedAt */
3963
4639
  createdAt: string
3964
- postings: Array<{
3965
- id: string
3966
- accountId: string
3967
- amount: string
3968
- amountDecimal?: string
3969
- direction?: string
3970
- token?: {
3971
- [key: string]: unknown
3972
- } | null
3973
- account?: {
3974
- [key: string]: unknown
3975
- } | null
3976
- }>
4640
+ }
4641
+
4642
+ export interface LedgerEntryToken {
4643
+ id: string
4644
+ code: string
4645
+ symbol: string
4646
+ decimals: number
4647
+ /** Network.slug */
4648
+ network: string
4649
+ /** null у нативного актива сети */
4650
+ contractAddress: string | null
4651
+ }
4652
+
4653
+ export interface LedgerPosting {
4654
+ id: string
4655
+ accountId: string
4656
+ /** available / reserved — счета адресов; external / adjustment — счета уровня Vault'а (без адреса): встречная сторона двойной записи */
4657
+ accountPurpose: "available" | "reserved" | "external" | "adjustment"
4658
+ /** address — счёт адреса; vault — счёт уровня Vault'а (external/adjustment) */
4659
+ ownerType: "address" | "vault"
4660
+ /** addressId для ownerType=address, иначе vaultId */
4661
+ ownerId: string
4662
+ /** null у vault-level счетов */
4663
+ addressId: string | null
4664
+ /** Raw smallest units со знаком: "2500000" | "-2500000" */
4665
+ amountRawSigned: string
4666
+ /** Та же сумма в единицах актива, со знаком: "-2.5". Если токена постинга нет в реестре (чего при живом FK быть не должно), сюда попадает raw — значение совпадёт с amountRawSigned */
4667
+ amountDecimalSigned: string
4668
+ }
4669
+
4670
+ export interface PatchReportTemplateBody {
4671
+ /** length: 1..120 */
4672
+ name?: string
4673
+ /** length: 0..500 */
4674
+ description?: string
4675
+ reportType?: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics"
4676
+ /** Параметры отчёта, как в query живого отчёта. Вместо from/to можно задать relativePeriod {unit: day|week|month, offset} — даты вычислятся при запуске. В ответе лежат под именем `parametersJson` */
4677
+ parameters?: {
4678
+ [key: string]: unknown
4679
+ }
4680
+ /** true — только admin */
4681
+ isShared?: boolean
4682
+ }
4683
+
4684
+ export interface ReportDrilldownBody {
4685
+ reportType?: string
4686
+ /** @defaultValue {} */
4687
+ filters?: {
4688
+ [key: string]: unknown
4689
+ }
4690
+ /** @defaultValue {} */
4691
+ drilldown?: {
4692
+ [key: string]: unknown
4693
+ }
4694
+ snapshotId?: string | null
4695
+ /**
4696
+ * range: 1..500
4697
+ * @defaultValue 100
4698
+ */
4699
+ limit?: number
4700
+ cursor?: string | null
3977
4701
  }
3978
4702
 
3979
4703
  export interface ReportExport {
3980
4704
  id: string
4705
+ vaultId: string
3981
4706
  snapshotId: string
4707
+ /** json | csv */
3982
4708
  format: string
4709
+ /** queued | processing | completed | failed; экспорт синхронный, поэтому сейчас всегда completed */
3983
4710
  status: string
3984
- filename: string | null
4711
+ requestedById: string
4712
+ requestedAt: string
4713
+ completedAt: string | null
4714
+ /** Задел под R2; сейчас всегда null */
4715
+ storageKey: string | null
3985
4716
  mimeType: string | null
4717
+ filename: string | null
4718
+ /** Длина содержимого в СИМВОЛАХ, а не в байтах: для не-ASCII текста (кириллица в подписях адресов) файл на диске будет больше */
3986
4719
  sizeBytes: number | null
4720
+ /** sha256 содержимого файла */
3987
4721
  fileHash: string | null
3988
- requestedById: string
3989
- completedAt: string | null
4722
+ errorCode: string | null
4723
+ errorMessage: string | null
3990
4724
  }
3991
4725
 
3992
- export interface ReportSnapshot {
4726
+ export interface ReportSnapshotDetail {
3993
4727
  id: string
3994
- vaultId: string
3995
4728
  /** snake_case, как и везде */
3996
4729
  reportType: string
3997
- status: string
3998
- schemaVersion: string | null
4730
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
4731
+ schemaVersion: string
4732
+ /** Тело `parameters` запроса создания, как есть */
3999
4733
  parametersJson: {
4000
4734
  [key: string]: unknown
4001
- } | null
4002
- /** Хеш нормализованных параметров — одинаковый запрос даёт одинаковый хеш */
4003
- normalizedParametersHash: string | null
4004
- result?: {
4005
- [key: string]: unknown
4006
- } | null
4007
- /** canonical sha256 результата — доказательство, что содержимое не подменили */
4008
- resultHash: string | null
4009
- /** Задел под R2; сейчас всегда null */
4010
- resultStorageKey: string | null
4011
- rowCount: number | null
4012
- /** Манифест: ключи операций, вошедших в снапшот (для drill-down по снапшоту) */
4013
- includedOperationKeysJson?: string[] | null
4014
- includedOperationKeysStorageKey?: string | null
4015
- dataCutoffAt: string | null
4735
+ }
4736
+ generatedById: string
4737
+ generatedAt: string
4738
+ /** Срез данных: операции новее в снапшот не вошли. Совпадает с generatedAt */
4739
+ dataCutoffAt: string
4740
+ /** Число операций в манифесте; 0, если манифеста нет — у отчётов без пооперационной выборки (balance_position, approval_activity) */
4741
+ rowCount: number
4742
+ /** sha256 canonical JSON результата — доказательство, что содержимое не подменили */
4743
+ resultHash: string
4744
+ /** generating | completed | failed; создание синхронное, поэтому сейчас всегда completed */
4745
+ status: string
4746
+ createdAt: string
4747
+ vaultId: string
4748
+ /** sha256 canonical JSON параметров — одинаковый запрос даёт одинаковый хеш */
4749
+ normalizedParametersHash: string
4750
+ /** Задел; сейчас всегда null */
4016
4751
  dataCutoffBlock: number | null
4752
+ /** current | historical — из параметра counterpartyClassification, по умолчанию historical */
4017
4753
  counterpartyClassificationMode: string | null
4754
+ /** current | historical — из параметра folderClassification, по умолчанию historical */
4018
4755
  folderClassificationMode: string | null
4756
+ /** Задел под R2; сейчас всегда null */
4757
+ resultStorageKey: string | null
4758
+ /** Манифест: ключи операций, вошедших в снапшот (по нему строится drill-down по снапшоту). null — у отчёта нет пооперационной выборки или манифест собрать не удалось */
4759
+ includedOperationKeysJson: string[] | null
4760
+ /** Задел под R2; сейчас всегда null */
4761
+ includedOperationKeysStorageKey: string | null
4019
4762
  errorCode: string | null
4020
4763
  errorMessage: string | null
4764
+ /** В ответах всегда null: скрытый (soft-delete) снапшот не отдаётся вовсе — 404 */
4765
+ deletedAt: string | null
4766
+ /** Результат отчёта, зафиксированный при создании. В ответе создания то же самое называется `result`. null возможен только у снапшота, который не достроился (status ≠ completed) */
4767
+ resultJson: {
4768
+ [key: string]: unknown
4769
+ } | null
4770
+ }
4771
+
4772
+ export interface ReportSnapshotListItem {
4773
+ id: string
4774
+ /** snake_case, как и везде */
4775
+ reportType: string
4776
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
4777
+ schemaVersion: string
4778
+ /** Тело `parameters` запроса создания, как есть */
4779
+ parametersJson: {
4780
+ [key: string]: unknown
4781
+ }
4782
+ generatedById: string
4783
+ generatedAt: string
4784
+ /** Срез данных: операции новее в снапшот не вошли. Совпадает с generatedAt */
4785
+ dataCutoffAt: string
4786
+ /** Число операций в манифесте; 0, если манифеста нет — у отчётов без пооперационной выборки (balance_position, approval_activity) */
4787
+ rowCount: number
4788
+ /** sha256 canonical JSON результата — доказательство, что содержимое не подменили */
4789
+ resultHash: string
4790
+ /** generating | completed | failed; создание синхронное, поэтому сейчас всегда completed */
4791
+ status: string
4792
+ createdAt: string
4793
+ }
4794
+
4795
+ export interface ReportSnapshotList {
4796
+ items: ReportSnapshotListItem[]
4797
+ }
4798
+
4799
+ export interface ReportSnapshot {
4800
+ id: string
4801
+ /** snake_case, как и везде */
4802
+ reportType: string
4803
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
4804
+ schemaVersion: string
4805
+ /** Тело `parameters` запроса создания, как есть */
4806
+ parametersJson: {
4807
+ [key: string]: unknown
4808
+ }
4021
4809
  generatedById: string
4022
4810
  generatedAt: string
4811
+ /** Срез данных: операции новее в снапшот не вошли. Совпадает с generatedAt */
4812
+ dataCutoffAt: string
4813
+ /** Число операций в манифесте; 0, если манифеста нет — у отчётов без пооперационной выборки (balance_position, approval_activity) */
4814
+ rowCount: number
4815
+ /** sha256 canonical JSON результата — доказательство, что содержимое не подменили */
4816
+ resultHash: string
4817
+ /** generating | completed | failed; создание синхронное, поэтому сейчас всегда completed */
4818
+ status: string
4023
4819
  createdAt: string
4820
+ vaultId: string
4821
+ /** sha256 canonical JSON параметров — одинаковый запрос даёт одинаковый хеш */
4822
+ normalizedParametersHash: string
4823
+ /** Задел; сейчас всегда null */
4824
+ dataCutoffBlock: number | null
4825
+ /** current | historical — из параметра counterpartyClassification, по умолчанию historical */
4826
+ counterpartyClassificationMode: string | null
4827
+ /** current | historical — из параметра folderClassification, по умолчанию historical */
4828
+ folderClassificationMode: string | null
4829
+ /** Задел под R2; сейчас всегда null */
4830
+ resultStorageKey: string | null
4831
+ /** Манифест: ключи операций, вошедших в снапшот (по нему строится drill-down по снапшоту). null — у отчёта нет пооперационной выборки или манифест собрать не удалось */
4832
+ includedOperationKeysJson: string[] | null
4833
+ /** Задел под R2; сейчас всегда null */
4834
+ includedOperationKeysStorageKey: string | null
4835
+ errorCode: string | null
4836
+ errorMessage: string | null
4837
+ /** В ответах всегда null: скрытый (soft-delete) снапшот не отдаётся вовсе — 404 */
4024
4838
  deletedAt: string | null
4839
+ /** Результат отчёта — тот же объект, что вернул бы живой отчёт. ТОЛЬКО в ответе создания; деталь отдаёт его как `resultJson` */
4840
+ result: {
4841
+ [key: string]: unknown
4842
+ }
4025
4843
  }
4026
4844
 
4027
4845
  export interface ReportTemplate {
4028
4846
  id: string
4029
4847
  vaultId: string
4848
+ /** Создатель шаблона */
4849
+ ownerId: string
4030
4850
  name: string
4851
+ description: string | null
4031
4852
  reportType: string
4853
+ /** Тело `parameters` запроса создания, как есть (включая relativePeriod) */
4032
4854
  parametersJson: {
4033
4855
  [key: string]: unknown
4034
- } | null
4856
+ }
4035
4857
  isShared: boolean
4036
- createdById: string
4037
4858
  createdAt: string
4038
- updatedAt?: string
4859
+ updatedAt: string
4039
4860
  }