@v3finance/core-sdk 0.1.0 → 0.2.0

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 +397 -7
  2. package/README.md +215 -72
  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 +21 -3
  12. package/dist/cjs/core/gated.d.ts.map +1 -1
  13. package/dist/cjs/core/gated.js +12 -3
  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 +130 -40
  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 +948 -172
  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 +22 -8
  31. package/dist/cjs/resources/addresses.d.ts.map +1 -1
  32. package/dist/cjs/resources/addresses.js +7 -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 +26 -4
  86. package/dist/cjs/resources/transfers.d.ts.map +1 -1
  87. package/dist/cjs/resources/transfers.js +23 -1
  88. package/dist/cjs/resources/transfers.js.map +1 -1
  89. package/dist/cjs/resources/types.d.ts +20 -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 +21 -3
  105. package/dist/esm/core/gated.d.ts.map +1 -1
  106. package/dist/esm/core/gated.js +12 -3
  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 +131 -41
  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 +948 -172
  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 +22 -8
  124. package/dist/esm/resources/addresses.d.ts.map +1 -1
  125. package/dist/esm/resources/addresses.js +7 -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 +26 -4
  179. package/dist/esm/resources/transfers.d.ts.map +1 -1
  180. package/dist/esm/resources/transfers.js +23 -1
  181. package/dist/esm/resources/transfers.js.map +1 -1
  182. package/dist/esm/resources/types.d.ts +20 -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 +191 -0
  190. package/docs/errors.md +471 -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 +22 -4
  195. package/src/core/http.ts +156 -46
  196. package/src/core/pagination.ts +38 -1
  197. package/src/generated/operations.ts +1 -1
  198. package/src/generated/types.ts +983 -173
  199. package/src/index.ts +64 -7
  200. package/src/resources/addresses.ts +23 -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 +33 -5
  215. package/src/resources/types.ts +20 -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 */
@@ -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 */
@@ -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 — цепочка применена сразу, без заявки */
@@ -1838,6 +1863,8 @@ export type EvaluationResult = {
1838
1863
  export interface PolicyTrace {
1839
1864
  policyId: string
1840
1865
  policyCode: string
1866
+ /** Редакция политики, которая оценивалась (Ф4) — та же, что попадёт в `decisionPolicyVersionId` перевода. null — у политики ещё нет версий */
1867
+ policyVersionId: string | null
1841
1868
  policyName: string
1842
1869
  effect: string
1843
1870
  applied: boolean
@@ -2546,21 +2573,38 @@ export interface Participant {
2546
2573
  userId: string
2547
2574
  user: UserCard | null
2548
2575
  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
- }>
2576
+ /** Источников может быть НЕСКОЛЬКО сразу; итоговая роль — максимум прав */
2577
+ via: ParticipantVia[]
2560
2578
  /** true — есть прямой грант, участника можно убрать одной операцией */
2561
2579
  removable: boolean
2562
2580
  }
2563
2581
 
2582
+ /**
2583
+ * ОТКУДА доступ — и, следовательно, что делать, чтобы его убрать:
2584
+ * - direct — прямой грант, отзывается точечно
2585
+ * - user_group — грант выдан ГРУППЕ пользователей: точечно не отзывается, нужно вывести человека из группы
2586
+ * - address_group — доступ через папку, куда входит адрес (у участников самой папки не встречается)
2587
+ */
2588
+ export type ParticipantVia = {
2589
+ kind: "direct"
2590
+ /** Грант на сам объект — отзывается точечно */
2591
+ grantId: string
2592
+ } | {
2593
+ kind: "user_group"
2594
+ /** Грант, выданный группе пользователей */
2595
+ grantId: string
2596
+ groupId: string
2597
+ /** Имя группы — чтобы объяснить «доступ у вас от группы Казначеи» */
2598
+ groupName: string
2599
+ } | {
2600
+ kind: "address_group"
2601
+ /** Грант, выданный на папку */
2602
+ grantId: string
2603
+ /** Папка, в которой лежит адрес */
2604
+ groupId: string
2605
+ groupName: string
2606
+ }
2607
+
2564
2608
  /** Кто имеет доступ к объекту — списком ЛЮДЕЙ, а не грантов. Админы не включаются: их доступ надзорный, есть ко всему сразу и совместным доступом не является */
2565
2609
  export interface ParticipantsResponse {
2566
2610
  /** Владелец объекта: у адреса — создатель, у папки — менеджер. Не участник */
@@ -2798,6 +2842,7 @@ export interface PendingApproval {
2798
2842
 
2799
2843
  export interface PolicyApprover {
2800
2844
  id: string
2845
+ policyId: string
2801
2846
  /** "user" | "group" */
2802
2847
  kind: string
2803
2848
  userId: string | null
@@ -2832,6 +2877,14 @@ export interface PolicyDetail {
2832
2877
  createdAt: string
2833
2878
  /** format: date-time */
2834
2879
  updatedAt: string
2880
+ /**
2881
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
2882
+ *
2883
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
2884
+ *
2885
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
2886
+ */
2887
+ currentVersionId: string | null
2835
2888
  /** LEGACY: движок их не читает, решает conditionJson */
2836
2889
  rules: Rule[]
2837
2890
  /** Явные одобряющие (для allow-политики с approval{}) */
@@ -2982,6 +3035,14 @@ export interface Policy {
2982
3035
  createdAt: string
2983
3036
  /** format: date-time */
2984
3037
  updatedAt: string
3038
+ /**
3039
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
3040
+ *
3041
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
3042
+ *
3043
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
3044
+ */
3045
+ currentVersionId: string | null
2985
3046
  }
2986
3047
 
2987
3048
  export interface PolicyTableItem {
@@ -3000,10 +3061,6 @@ export interface PolicyTableItem {
3000
3061
  adminBypass: boolean
3001
3062
  allowInitiatorApproval: boolean
3002
3063
  enabled: boolean
3003
- /** Условие применимости — булево дерево. ЭТО ЧИТАЕТ ДВИЖОК. null — политика применима всегда. Legacy-таблица rules движком не используется и удаляется в Ф5 */
3004
- conditionJson: PolicyCondition | null
3005
- /** Требование подписи. null — политика разрешает без одобрения */
3006
- approvalJson: PolicyApprovalInput | null
3007
3064
  /** format: date-time */
3008
3065
  createdAt: string
3009
3066
  /** format: date-time */
@@ -3065,6 +3122,14 @@ export interface PolicyWithRules {
3065
3122
  createdAt: string
3066
3123
  /** format: date-time */
3067
3124
  updatedAt: string
3125
+ /**
3126
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
3127
+ *
3128
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
3129
+ *
3130
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
3131
+ */
3132
+ currentVersionId: string | null
3068
3133
  /** LEGACY: движок их не читает, решает conditionJson */
3069
3134
  rules: Rule[]
3070
3135
  /** Явные одобряющие (для allow-политики с approval{}) */
@@ -3184,21 +3249,45 @@ export interface SeatCreated {
3184
3249
  expiresAt: string
3185
3250
  }
3186
3251
 
3252
+ export interface ShareBatchAllRejected {
3253
+ /** items: 1..∞ */
3254
+ results: ShareBatchRejectedItem[]
3255
+ }
3256
+
3257
+ export interface ShareBatchRejectedItem {
3258
+ subjectType: "user" | "group"
3259
+ subjectUserId: string | null
3260
+ subjectGroupId: string | null
3261
+ ok: false
3262
+ outcome: null
3263
+ grant: null
3264
+ error: {
3265
+ /** SELF_SHARE — себе; OWNER_SUBJECT — владельцу адреса; NOT_MEMBER — не участник Vault'а; NOT_VISIBLE — получатель вам не виден (нет гранта видимости); GROUP_NOT_FOUND — группы нет в этом Vault'е */
3266
+ code: string
3267
+ /** Сейчас дублирует code — человеческий текст собирает клиент */
3268
+ message: string
3269
+ }
3270
+ }
3271
+
3272
+ export interface ShareBatchGrantedItem {
3273
+ subjectType: "user" | "group"
3274
+ subjectUserId: string | null
3275
+ subjectGroupId: string | null
3276
+ ok: true
3277
+ /** created — новый грант; updated — у действующего гранта сменили права; reactivated — снят отзыв с ранее отозванного */
3278
+ outcome: "created" | "updated" | "reactivated"
3279
+ grant: AddressAccessGrant
3280
+ error: null
3281
+ }
3282
+
3283
+ export type ShareBatchItem = ShareBatchGrantedItem | ShareBatchRejectedItem
3284
+
3187
3285
  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
- }>
3286
+ results: ShareBatchItem[]
3200
3287
  }
3201
3288
 
3289
+ export type ShareGrantCreated = AddressAccessGrant | ShareBatchResult
3290
+
3202
3291
  export interface Token {
3203
3292
  /** cuid */
3204
3293
  id: string
@@ -3311,6 +3400,16 @@ export interface TransactionDetail {
3311
3400
  amlScore: number | null
3312
3401
  /** Когда провайдер сделал проверку */
3313
3402
  amlCheckedAt: string | null
3403
+ /** USD-оценка перевода: amountDecimal × usdRate, decimal-СТРОКА. Считается один раз при создании и live-курсом не пересчитывается; null — курс был недоступен */
3404
+ usdValue: string | null
3405
+ /** Редакция политики, принявшая решение (PolicyVersion.id, Ф4). ЕДИНСТВЕННЫЙ способ связать перевод с правилами, которые действовали в момент решения: `decisionPolicyId` ведёт на политику, а её с тех пор могли переписать. Снимок — GET /policies/{id}/versions. null — решение принято до введения версий */
3406
+ decisionPolicyVersionId: string | null
3407
+ /** Группы контрагентов получателя НА МОМЕНТ РЕШЕНИЯ (id групп). Версия политики хранит id групп белого списка, но не их состав — без этого снимка нельзя объяснить, почему платёж прошёл, после того как контрагента из группы убрали. null — получатель не был контрагентом */
3408
+ decisionCounterpartyGroupsJson: string[] | null
3409
+ /** Контрагент, сопоставленный по toAddress при создании. Дублирует `counterparty.id` — отдаётся как колонка перевода */
3410
+ counterpartyId: string | null
3411
+ /** Клиентский ключ попытки, с которым перевод создан (см. тело POST /transfers). null — ключ не передавали */
3412
+ idempotencyKey: string | null
3314
3413
  amountDecimal: string
3315
3414
  assetSymbol: string
3316
3415
  }
@@ -3408,7 +3507,7 @@ export interface UserProfile {
3408
3507
  color: string | null
3409
3508
  /** Emoji-иконка */
3410
3509
  emoji: string | null
3411
- /** true — аккаунт создан по пригласительной ссылке и настоящая почта ещё НЕ указана (в email лежит технический адрес). Клиент обязан попросить адрес: пока его нет, человеку некуда прислать magic-link, и при истечении сессии он потеряет доступ. Привязка — POST /me/email */
3510
+ /** true — аккаунт создан по пригласительной ссылке и настоящая почта ещё НЕ указана (в email лежит технический адрес). Клиент обязан попросить адрес: пока его нет, человеку некуда прислать magic-link, и при истечении сессии он потеряет доступ. Привязка — POST /me/profile/email. Приходит и в GET, и в ответе PATCH */
3412
3511
  needsEmail: boolean
3413
3512
  /** format: date-time */
3414
3513
  createdAt: string
@@ -3498,6 +3597,7 @@ export interface VaultPoliciesPage {
3498
3597
  items: Array<{
3499
3598
  /** cuid */
3500
3599
  id: string
3600
+ groupId: string
3501
3601
  code: string
3502
3602
  name: string
3503
3603
  description: string | null
@@ -3519,11 +3619,17 @@ export interface VaultPoliciesPage {
3519
3619
  createdAt: string
3520
3620
  /** format: date-time */
3521
3621
  updatedAt: string
3622
+ /**
3623
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
3624
+ *
3625
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
3626
+ *
3627
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
3628
+ */
3629
+ currentVersionId: string | null
3522
3630
  _count: {
3523
3631
  /** range: -9007199254740991..9007199254740991 */
3524
3632
  rules: number
3525
- /** range: -9007199254740991..9007199254740991 */
3526
- limits?: number
3527
3633
  }
3528
3634
  /** Owner-группа политики */
3529
3635
  group: GroupRef
@@ -3572,8 +3678,12 @@ export interface VisibilityGrant {
3572
3678
  } | null
3573
3679
  status: string
3574
3680
  grantorId: string
3681
+ revokedAt: string | null
3682
+ revokedById: string | null
3575
3683
  /** format: date-time */
3576
3684
  createdAt: string
3685
+ /** format: date-time */
3686
+ updatedAt: string
3577
3687
  }
3578
3688
 
3579
3689
  export interface VoteBody {
@@ -3649,36 +3759,111 @@ export type WebhookResult = {
3649
3759
  eventId: string
3650
3760
  }
3651
3761
 
3652
- export interface ActivityReport {
3762
+ export interface AccountStatementReport {
3653
3763
  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
3764
+ balancesByAsset: StatementTokenRow[]
3765
+ timeline: Array<{
3766
+ /** Начало интервала (как periodStart у остальных) */
3767
+ period: string
3768
+ /** Задел; сейчас всегда пустая строка */
3769
+ opening: string
3770
+ /** Приток ТЕКСТОМ для печати: "1.5 USDT; 20 TRX" — сумма с символом, активы через «; ». Числом не разбирать. Перечислен КАЖДЫЙ актив, по которому в интервале были операции, даже если притока по нему не было: тогда он приходит как "0 USDT", а не пропадает */
3771
+ incoming: string
3772
+ /** Отток, в том же текстовом виде и с теми же нулевыми позициями */
3773
+ outgoing: string
3774
+ /** Задел; сейчас всегда "0" */
3775
+ adjustments: string
3776
+ /** Задел; сейчас всегда пустая строка */
3777
+ closing: string
3778
+ }>
3779
+ dataQuality: DataQuality
3666
3780
  }
3667
3781
 
3668
3782
  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
3783
+ /** Задел под сохранённый отчёт. У живого отчёта всегда null */
3784
+ id: string | null
3785
+ /** Тип отчёта, snake_case — тот же, что в пути и в ?type= */
3786
+ type: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics"
3680
3787
  generatedAt: string
3681
- generatedById?: string | null
3788
+ /** Кто запросил отчёт */
3789
+ generatedById: string
3790
+ /** Начало периода (включительно), ISO-8601 UTC. У balance_position from = to = момент позиции */
3791
+ from: string
3792
+ /** Конец периода (НЕ включительно), ISO-8601 UTC */
3793
+ to: string
3794
+ /** IANA-зона, в которой нарезан timeline. У balance_position всегда UTC */
3795
+ timezone: string
3796
+ /** По какой дате операция отнесена к периоду. Если параметр не прислан: blockchainAt у counterparty_activity и counterparty_group_activity, createdAt у остальных */
3797
+ dateBasis: "createdAt" | "blockchainAt" | "confirmedAt"
3798
+ baseCurrency: "USD"
3799
+ /** Значение параметра counterpartyClassification (по умолчанию historical). У balance_position всегда current */
3800
+ classificationMode: "current" | "historical"
3801
+ dataCutoff: {
3802
+ /** Операции новее в отчёт не вошли */
3803
+ timestamp: string
3804
+ /** Задел; сейчас всегда null */
3805
+ blockNumber: number | null
3806
+ }
3807
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
3808
+ schemaVersion: string
3809
+ }
3810
+
3811
+ export interface StatementTokenRow {
3812
+ tokenId: string
3813
+ assetCode: string
3814
+ assetSymbol: string
3815
+ /** Network.slug */
3816
+ network: string
3817
+ openingAvailable: string
3818
+ openingReserved: string
3819
+ openingTotal: string
3820
+ incomingExternal: string
3821
+ outgoingExternal: string
3822
+ incomingInternal: string
3823
+ outgoingInternal: string
3824
+ /** Ручные корректировки ledger (kind=adjustment) за период */
3825
+ adjustments: string
3826
+ closingAvailable: string
3827
+ closingReserved: string
3828
+ closingTotal: string
3829
+ /** (opening + движения + adjustments) − closing из ledger. Ожидается "0"; ненулевое значение — операции и ledger разошлись, и оно же считается в dataQuality.ledgerReconciliationDifferences */
3830
+ reconciliationDifference: string
3831
+ }
3832
+
3833
+ export interface DataQuality {
3834
+ totalOperations: number
3835
+ unmatchedOperations: number
3836
+ retroactivelyMatchedOperations: number
3837
+ operationsWithoutUsdRate: number
3838
+ operationsWithoutSelectedDate: number
3839
+ operationsWithoutBlockchainTime: number
3840
+ operationsWithoutPaymentPurpose: number
3841
+ operationsWithoutBusinessReference: number
3842
+ uncategorizedOperations: number
3843
+ conflictingCounterpartyClassifications: number
3844
+ invalidOperationRelations: number
3845
+ missingLedgerEntries: number
3846
+ ledgerReconciliationDifferences: number
3847
+ }
3848
+
3849
+ /** УСТАРЕЛО: используйте схему конкретного отчёта (TransactionActivityReport, AssetMovementReport, …) или AnyReport. Имя сохранено для совместимости сгенерированных клиентов: это объединение девяти отчётов, кроме category_analytics */
3850
+ export type ActivityReport = TransactionActivityReport | CounterpartyActivityReport | CounterpartyGroupActivityReport | AssetMovementReport | AddressActivityReport | AccountStatementReport | OperationsExceptionsReport | ApprovalActivityReport | BalancePositionReport
3851
+
3852
+ export interface TransactionActivityReport {
3853
+ report: ReportMeta
3854
+ summary: ReportSummary
3855
+ byAsset: ByAssetRow[]
3856
+ byCounterparty: ByCounterpartyRow[]
3857
+ byOwnAddress: ByOwnAddressRow[]
3858
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
3859
+ byAddressOwner?: ByAddressOwnerRow[]
3860
+ byStatus: ByStatusRow[]
3861
+ byCategory: ByCategoryRow[]
3862
+ timeline: TimelinePoint[]
3863
+ approvals: ReportApprovals
3864
+ comparison: Comparison
3865
+ observations: Observation[]
3866
+ dataQuality: DataQuality
3682
3867
  }
3683
3868
 
3684
3869
  export interface ReportSummary {
@@ -3709,32 +3894,39 @@ export interface ReportSummary {
3709
3894
  minimumAmountByAsset: AssetAmount[]
3710
3895
  /** @defaultValue [] */
3711
3896
  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
3897
+ confirmedCount: number
3898
+ pendingCount: number
3899
+ awaitingApprovalCount: number
3900
+ rejectedCount: number
3901
+ cancelledCount: number
3902
+ failedCount: number
3903
+ /** Число календарных дней (в timezone отчёта) с хотя бы одной операцией */
3904
+ activeDays: number
3905
+ /** createdAt самой ранней операции выборки; null — выборка пуста */
3906
+ firstOperationAt: string | null
3907
+ /** createdAt самой поздней операции выборки; null — выборка пуста */
3908
+ lastOperationAt: string | null
3721
3909
  }
3722
3910
 
3723
3911
  export interface AssetAmount {
3724
- tokenId: string | null
3725
- assetCode: string | null
3912
+ tokenId: string
3913
+ /** Token.code из реестра */
3914
+ assetCode: string
3726
3915
  assetSymbol: string
3727
- network: string | null
3728
- contractAddress?: string | null
3729
- /** raw smallest units, строкой */
3916
+ /** Network.slug */
3917
+ network: string
3918
+ /** DECIMAL-строка в единицах актива ("1.5"), НЕ raw smallest units; хвостовые нули убраны. Со знаком только у netFlow* ("-12.5" — отток больше притока), у остальных блоков неотрицательна */
3730
3919
  amount: string
3731
3920
  }
3732
3921
 
3733
3922
  export interface ByAssetRow {
3734
- tokenId: string | null
3735
- assetCode: string | null
3923
+ tokenId: string
3924
+ /** Token.code из реестра */
3925
+ assetCode: string
3736
3926
  assetSymbol: string
3737
- network: string | null
3927
+ /** Network.slug */
3928
+ network: string
3929
+ /** null у нативного актива сети */
3738
3930
  contractAddress: string | null
3739
3931
  incoming: string
3740
3932
  outgoing: string
@@ -3758,8 +3950,11 @@ export interface ByAssetRow {
3758
3950
  }
3759
3951
 
3760
3952
  export interface ByCounterpartyRow {
3953
+ /** null — операции без сопоставленного контрагента (одна строка на все) */
3761
3954
  counterpartyId: string | null
3955
+ /** Имя контрагента. У строки без контрагента (counterpartyId = null) сюда приходит английская заглушка "unmatched counterparty" — она не локализуется, поэтому различать такую строку надо по counterpartyId */
3762
3956
  name: string | null
3957
+ /** Свободная категория контрагента (exchange, client…); null — не задана */
3763
3958
  kind: string | null
3764
3959
  /** @defaultValue [] */
3765
3960
  incomingByAsset: AssetAmount[]
@@ -3781,7 +3976,7 @@ export interface ByOwnAddressRow {
3781
3976
  [key: string]: unknown
3782
3977
  } | null
3783
3978
  /**
3784
- * Папки, в которых адрес состоял (зависит от folderClassification)
3979
+ * Задел под папки адреса. Сейчас ВСЕГДА пустой массив
3785
3980
  * @defaultValue []
3786
3981
  */
3787
3982
  folderSnapshots: unknown[]
@@ -3794,6 +3989,20 @@ export interface ByOwnAddressRow {
3794
3989
  operationsCount: number
3795
3990
  }
3796
3991
 
3992
+ export interface ByAddressOwnerRow {
3993
+ /** Владелец адресов (создатель) */
3994
+ userId: string
3995
+ /** Считается от вызывающего: у двух людей один отчёт разложится по-разному */
3996
+ isMe: boolean
3997
+ /** Сколько адресов этого владельца попало в выборку */
3998
+ addressesCount: number
3999
+ incomingByAsset: AssetAmount[]
4000
+ outgoingByAsset: AssetAmount[]
4001
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4002
+ grossTurnoverUsd: string | null
4003
+ operationsCount: number
4004
+ }
4005
+
3797
4006
  export interface ByStatusRow {
3798
4007
  status: string
3799
4008
  count: number
@@ -3814,17 +4023,6 @@ export interface ByCategoryRow {
3814
4023
  usdValue: string | null
3815
4024
  }
3816
4025
 
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
4026
  export interface TimelinePoint {
3829
4027
  periodStart: string
3830
4028
  periodEnd: string
@@ -3854,75 +4052,456 @@ export interface ReportApprovals {
3854
4052
  maximumApprovalSeconds: number | null
3855
4053
  }
3856
4054
 
4055
+ export interface Comparison {
4056
+ /** none | previous_period | previous_year */
4057
+ mode: string
4058
+ previousFrom: string | null
4059
+ previousTo: string | null
4060
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
4061
+ incomingUsdChangePercent: string | null
4062
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
4063
+ outgoingUsdChangePercent: string | null
4064
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
4065
+ grossTurnoverUsdChangePercent: string | null
4066
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
4067
+ operationsCountChangePercent: string | null
4068
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
4069
+ averageOperationUsdChangePercent: string | null
4070
+ }
4071
+
3857
4072
  export interface Observation {
4073
+ /** Машиночитаемый код: COUNTERPARTY_TURNOVER_INCREASED, SINGLE_OPERATION_CONCENTRATION, UNMATCHED_OPERATIONS_PRESENT… */
3858
4074
  code: string
3859
- severity: string
4075
+ severity: "info" | "warning"
3860
4076
  title: string
3861
4077
  description: string
3862
4078
  metric: string | null
3863
4079
  currentValue: string | null
4080
+ /** Задел; сейчас всегда null */
3864
4081
  comparisonValue: string | null
3865
- changePercent: number | null
3866
- drilldownFilters?: {
4082
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — наблюдение не про сравнение периодов (концентрация, качество данных, зависшие операции). Не null только у наблюдений о росте или падении оборота и среднего платежа — там это то же значение, что в comparison */
4083
+ changePercent: string | null
4084
+ /** Готовое уточнение для drill-down, раскрывающее наблюдение до операций; {} — раскрывать нечем */
4085
+ drilldownFilters: {
3867
4086
  [key: string]: unknown
3868
- } | null
4087
+ }
3869
4088
  }
3870
4089
 
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
4090
+ export interface CounterpartyActivityReport {
4091
+ report: ReportMeta
4092
+ /** Карточка контрагента на момент построения отчёта */
4093
+ counterparty: {
4094
+ id: string
4095
+ name: string
4096
+ kind: string | null
4097
+ /** Архивный контрагент отчёт не блокирует — история по нему остаётся */
4098
+ archived: boolean
4099
+ /** Адресов в справочнике у контрагента */
4100
+ addressesCount: number
4101
+ /** Группы контрагентов, в которых он состоит СЕЙЧАС */
4102
+ groupIds: string[]
4103
+ }
4104
+ summary: ReportSummary
4105
+ byAsset: ByAssetRow[]
4106
+ byCounterpartyAddress: ByCounterpartyAddressRow[]
4107
+ byOwnAddress: ByOwnAddressRow[]
4108
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
4109
+ byAddressOwner?: ByAddressOwnerRow[]
4110
+ byStatus: ByStatusRow[]
4111
+ byCategory: ByCategoryRow[]
4112
+ timeline: TimelinePoint[]
4113
+ approvals: ReportApprovals
4114
+ comparison: Comparison
4115
+ observations: Observation[]
4116
+ dataQuality: DataQuality
4117
+ }
4118
+
4119
+ export interface ByCounterpartyAddressRow {
4120
+ /** Запись справочника адресов контрагента; null — адрес в справочник не внесён */
4121
+ counterpartyAddressId: string | null
4122
+ /** Внешний адрес (on-chain) */
4123
+ address: string
4124
+ /** Network.slug */
4125
+ network: string
4126
+ /** Задел; сейчас всегда null */
4127
+ label: string | null
4128
+ incomingByAsset: AssetAmount[]
4129
+ outgoingByAsset: AssetAmount[]
4130
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4131
+ grossTurnoverUsd: string | null
4132
+ operationsCount: number
4133
+ incomingCount: number
4134
+ outgoingCount: number
4135
+ /** Доля адреса в USD-обороте отчёта, проценты decimal-строкой; null — нет USD-оценки или оборот нулевой */
4136
+ turnoverShareUsd: string | null
4137
+ firstOperationAt: string | null
4138
+ lastOperationAt: string | null
4139
+ /** true — до начала периода операций с этим адресом не было (в любом статусе, включая внутренние потоки) */
4140
+ isNewInPeriod: boolean
3885
4141
  }
3886
4142
 
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
4143
+ export interface CounterpartyGroupActivityReport {
4144
+ report: ReportMeta
4145
+ group: {
4146
+ id: string
4147
+ code: string
4148
+ name: string
4149
+ /** Сколько контрагентов в группе СЕЙЧАС. При counterpartyClassification=historical выборка идёт по составу на момент отчёта и может не совпасть с этим числом */
4150
+ counterpartiesCount: number
4151
+ }
4152
+ summary: ReportSummary
4153
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
4154
+ byAddressOwner?: ByAddressOwnerRow[]
4155
+ byCounterparty: ByGroupCounterpartyRow[]
4156
+ byAsset: ByAssetRow[]
4157
+ byStatus: ByStatusRow[]
4158
+ byCategory: ByCategoryRow[]
4159
+ timeline: TimelinePoint[]
4160
+ approvals: ReportApprovals
4161
+ comparison: Comparison
4162
+ observations: Observation[]
4163
+ dataQuality: DataQuality
4164
+ }
4165
+
4166
+ export interface ByGroupCounterpartyRow {
4167
+ counterpartyId: string | null
4168
+ name: string | null
4169
+ kind: string | null
4170
+ incomingByAsset: AssetAmount[]
4171
+ outgoingByAsset: AssetAmount[]
4172
+ /** Приток минус отток по активу, со знаком */
4173
+ netFlowByAsset: AssetAmount[]
4174
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4175
+ grossTurnoverUsd: string | null
4176
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4177
+ netFlowUsd: string | null
4178
+ operationsCount: number
4179
+ /** Доля контрагента в USD-обороте группы, проценты decimal-строкой ("37.5"); null — у оборота нет USD-оценки или он нулевой */
4180
+ shareOfGroupTurnoverUsd: string | null
4181
+ firstOperationAt: string | null
4182
+ lastOperationAt: string | null
3896
4183
  }
3897
4184
 
3898
- export interface BalancesAsOf {
3899
- /** Момент среза (echo параметра ?at=) */
4185
+ export interface AssetMovementReport {
4186
+ report: ReportMeta
4187
+ asset: {
4188
+ tokenId: string
4189
+ /** Token.code из реестра */
4190
+ code: string
4191
+ symbol: string
4192
+ name: string
4193
+ decimals: number
4194
+ /** Network.slug */
4195
+ network: string
4196
+ /** null у нативного актива сети */
4197
+ contractAddress: string | null
4198
+ }
4199
+ /** Остаток актива на начало периода (from), из ledger. DECIMAL в единицах актива — не raw, в отличие от openingBalances у address_activity */
4200
+ openingBalance: {
4201
+ /** decimal */
4202
+ available: string
4203
+ /** decimal */
4204
+ reserved: string
4205
+ /** available + reserved, decimal */
4206
+ total: string
4207
+ }
4208
+ /** Остаток актива на конец периода (to), из ledger. DECIMAL в единицах актива — не raw, в отличие от openingBalances у address_activity */
4209
+ closingBalance: {
4210
+ /** decimal */
4211
+ available: string
4212
+ /** decimal */
4213
+ reserved: string
4214
+ /** available + reserved, decimal */
4215
+ total: string
4216
+ }
4217
+ summary: ReportSummary
4218
+ byCounterparty: ByCounterpartyBriefRow[]
4219
+ byOwnAddress: ByOwnAddressRow[]
4220
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
4221
+ byAddressOwner?: ByAddressOwnerRow[]
4222
+ byStatus: ByStatusRow[]
4223
+ byCategory: ByCategoryRow[]
4224
+ timeline: TimelinePoint[]
4225
+ comparison: Comparison
4226
+ observations: Observation[]
4227
+ dataQuality: DataQuality
4228
+ }
4229
+
4230
+ export interface ByCounterpartyBriefRow {
4231
+ /** null — операции без сопоставленного контрагента (одна строка на все) */
4232
+ counterpartyId: string | null
4233
+ /** Имя контрагента. У строки без контрагента (counterpartyId = null) сюда приходит английская заглушка "unmatched counterparty" — она не локализуется, поэтому различать такую строку надо по counterpartyId */
4234
+ name: string | null
4235
+ incomingByAsset: AssetAmount[]
4236
+ outgoingByAsset: AssetAmount[]
4237
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4238
+ grossTurnoverUsd: string | null
4239
+ operationsCount: number
4240
+ }
4241
+
4242
+ export interface AddressActivityReport {
4243
+ report: ReportMeta
4244
+ /** Цель отчёта, и форма у неё ДВЕ: при addressId — карточка адреса, при folderId — {folder:{id,name}}. Имя ключа одно на оба случая исторически */
4245
+ address: {
4246
+ id: string
4247
+ address: string
4248
+ label: string | null
4249
+ description: string | null
4250
+ /** Без апострофов: m/44/195/… */
4251
+ derivationPath: string
4252
+ segmentsJson: {
4253
+ [key: string]: unknown
4254
+ }
4255
+ /** Network.slug */
4256
+ network: string
4257
+ createdAt: string
4258
+ } | {
4259
+ folder: {
4260
+ id: string
4261
+ name: string
4262
+ }
4263
+ }
4264
+ /** Папки адреса. Вторая дата зависит от folderClassification: historical (по умолчанию) — validFrom из истории членства, current — createdAt текущего членства. При folderId — всегда пустой массив */
4265
+ folderMemberships: Array<{
4266
+ folderId: string
4267
+ validFrom: string
4268
+ } | {
4269
+ folderId: string
4270
+ createdAt: string
4271
+ }>
4272
+ /** Балансы счетов на начало периода (from), RAW. При addressId — счета этого адреса. При folderId выборка НЕ сужается до папки: приходят все счета scope вызывающего (у admin — весь Vault, включая external и adjustment) */
4273
+ openingBalances: ReportAccountBalance[]
4274
+ /** Балансы счетов на конец периода (to), RAW. При addressId — счета этого адреса. При folderId выборка НЕ сужается до папки: приходят все счета scope вызывающего (у admin — весь Vault, включая external и adjustment) */
4275
+ closingBalances: ReportAccountBalance[]
4276
+ summary: ReportSummary
4277
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
4278
+ byAddressOwner?: ByAddressOwnerRow[]
4279
+ /** Доля адреса (папки) в USD-обороте всего scope за тот же период, проценты decimal-строкой; null — нет USD-оценки или оборот scope нулевой */
4280
+ shareOfUserActivityUsd: string | null
4281
+ byAsset: ByAssetRow[]
4282
+ byCounterparty: ByCounterpartyBriefRow[]
4283
+ byStatus: ByStatusRow[]
4284
+ byCategory: ByCategoryRow[]
4285
+ timeline: TimelinePoint[]
4286
+ observations: Observation[]
4287
+ dataQuality: DataQuality
4288
+ }
4289
+
4290
+ export interface ReportAccountBalance {
4291
+ accountId: string
4292
+ /** available / reserved — счета адресов; external / adjustment — счета уровня Vault'а (без адреса): встречная сторона двойной записи */
4293
+ purpose: "available" | "reserved" | "external" | "adjustment"
4294
+ tokenId: string
4295
+ /** null у счетов уровня Vault'а */
4296
+ addressId: string | null
4297
+ /** Raw smallest units со знаком — НЕ decimal */
4298
+ balanceRaw: string
4299
+ }
4300
+
4301
+ export interface OperationsExceptionsReport {
4302
+ report: ReportMeta
4303
+ summary: {
4304
+ operationsCount: number
4305
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4306
+ usdValue: string | null
4307
+ /** null — выборка пуста */
4308
+ oldestOperationAt: string | null
4309
+ /** Возраст считается от createdAt до момента построения отчёта */
4310
+ averageAgeSeconds: number | null
4311
+ medianAgeSeconds: number | null
4312
+ }
4313
+ /** Всегда шесть строк в этом порядке, включая пустые корзины */
4314
+ ageBuckets: Array<{
4315
+ bucket: "0-15m" | "15-60m" | "1-6h" | "6-24h" | "1-3d" | ">3d"
4316
+ count: number
4317
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4318
+ usdValue: string | null
4319
+ }>
4320
+ byStatus: ByStatusRow[]
4321
+ byAsset: ByAssetRow[]
4322
+ byCounterparty: Array<{
4323
+ /** null — операции без сопоставленного контрагента (одна строка на все) */
4324
+ counterpartyId: string | null
4325
+ /** Имя контрагента. У строки без контрагента (counterpartyId = null) сюда приходит английская заглушка "unmatched counterparty" — она не локализуется, поэтому различать такую строку надо по counterpartyId */
4326
+ name: string | null
4327
+ count: number
4328
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4329
+ usdValue: string | null
4330
+ }>
4331
+ byInitiator: Array<{
4332
+ /** null — у операции нет инициатора (депозиты) */
4333
+ initiatorId: string | null
4334
+ count: number
4335
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4336
+ usdValue: string | null
4337
+ }>
4338
+ byDecisionPolicy: Array<{
4339
+ /** null — решение по операции политика не принимала */
4340
+ decisionPolicyId: string | null
4341
+ code: string | null
4342
+ count: number
4343
+ }>
4344
+ dataQuality: DataQuality
4345
+ }
4346
+
4347
+ export interface ApprovalActivityReport {
4348
+ report: ReportMeta
4349
+ summary: {
4350
+ requestsCount: number
4351
+ pendingCount: number
4352
+ approvedCount: number
4353
+ rejectedCount: number
4354
+ cancelledCount: number
4355
+ /** approved / (approved + rejected) × 100, decimal-строка; null — решённых заявок нет. Отменённые в знаменатель не входят */
4356
+ approvalRatePercent: string | null
4357
+ rejectionRatePercent: string | null
4358
+ /** cancelled / все заявки × 100; null — заявок нет */
4359
+ cancellationRatePercent: string | null
4360
+ /** От создания заявки до resolvedAt; null — решённых нет */
4361
+ averageResolutionSeconds: number | null
4362
+ medianResolutionSeconds: number | null
4363
+ maximumResolutionSeconds: number | null
4364
+ /** От создания заявки до первого голоса; null — голосов нет */
4365
+ averageFirstVoteSeconds: number | null
4366
+ medianFirstVoteSeconds: number | null
4367
+ requestsWithoutVotes: number
4368
+ }
4369
+ byAction: Array<{
4370
+ /** ApprovalRequest.action: transfer, composite… */
4371
+ action: string
4372
+ count: number
4373
+ approvedCount: number
4374
+ rejectedCount: number
4375
+ cancelledCount: number
4376
+ pendingCount: number
4377
+ averageResolutionSeconds: number | null
4378
+ }>
4379
+ byInitiator: Array<{
4380
+ userId: string
4381
+ name: string
4382
+ requestsCount: number
4383
+ approvedCount: number
4384
+ rejectedCount: number
4385
+ cancelledCount: number
4386
+ averageResolutionSeconds: number | null
4387
+ }>
4388
+ byApprover: Array<{
4389
+ userId: string
4390
+ /** Имя; если пользователь не найден — его userId */
4391
+ name: string
4392
+ assignedRequestsCount: number
4393
+ votedCount: number
4394
+ approveVotes: number
4395
+ rejectVotes: number
4396
+ pendingVotes: number
4397
+ averageVoteSeconds: number | null
4398
+ medianVoteSeconds: number | null
4399
+ }>
4400
+ byDecisionPolicy: Array<{
4401
+ decisionPolicyId: string | null
4402
+ code: string | null
4403
+ /** Задел; сейчас всегда null */
4404
+ name: string | null
4405
+ triggeredCount: number
4406
+ approvedCount: number
4407
+ rejectedCount: number
4408
+ pendingCount: number
4409
+ averageResolutionSeconds: number | null
4410
+ }>
4411
+ timeline: Array<{
4412
+ periodStart: string
4413
+ createdCount: number
4414
+ /** approved + rejected; отменённые не входят */
4415
+ resolvedCount: number
4416
+ approvedCount: number
4417
+ rejectedCount: number
4418
+ cancelledCount: number
4419
+ }>
4420
+ /** Только эхо параметра comparison: процентов изменения этот отчёт НЕ считает. У живого отчёта previous_year приходит как none; в снапшоте и при запуске шаблона значение не переводится и приходит как прислано */
4421
+ comparison: {
4422
+ mode: "none" | "previous_period" | "previous_year"
4423
+ }
4424
+ dataQuality: DataQuality
4425
+ }
4426
+
4427
+ export interface BalancePositionReport {
4428
+ report: ReportMeta
4429
+ /** Момент позиции, ISO-8601 UTC */
3900
4430
  asOf: string
3901
- /** @defaultValue [] */
3902
- totals: Array<{
3903
- [key: string]: unknown
4431
+ totalsByAsset: Array<{
4432
+ tokenId: string
4433
+ assetCode: string
4434
+ assetSymbol: string
4435
+ /** Network.slug */
4436
+ network: string
4437
+ /** decimal */
4438
+ available: string
4439
+ /** decimal */
4440
+ reserved: string
4441
+ /** available + reserved, decimal */
4442
+ total: string
4443
+ usdRate: string | null
4444
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4445
+ usdValue: string | null
3904
4446
  }>
3905
- /** @defaultValue [] */
3906
- accounts: Array<{
3907
- [key: string]: unknown
4447
+ byAddress: Array<{
4448
+ addressId: string
4449
+ address: string
4450
+ label: string | null
4451
+ balances: Array<{
4452
+ tokenId: string
4453
+ /** decimal */
4454
+ available: string
4455
+ /** decimal */
4456
+ reserved: string
4457
+ /** decimal */
4458
+ total: string
4459
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4460
+ usdValue: string | null
4461
+ }>
3908
4462
  }>
4463
+ concentration: {
4464
+ /** Доля крупнейшего адреса в USD, проценты decimal-строкой; null без USD-оценки */
4465
+ largestAddressShareUsd: string | null
4466
+ /** Доля пяти крупнейших адресов, проценты decimal-строкой; null без USD-оценки */
4467
+ top5AddressesShareUsd: string | null
4468
+ }
4469
+ dataQuality: DataQuality
3909
4470
  }
3910
4471
 
4472
+ /** Один из десяти отчётов. Форму определяет report.type: у каждого типа своя схема, общих для всех блоков — только report и (кроме category_analytics) dataQuality */
4473
+ export type AnyReport = TransactionActivityReport | CounterpartyActivityReport | CounterpartyGroupActivityReport | AssetMovementReport | AddressActivityReport | AccountStatementReport | OperationsExceptionsReport | ApprovalActivityReport | BalancePositionReport | CategoryAnalytics
4474
+
3911
4475
  export interface CategoryAnalytics {
3912
- report: ReportMeta
4476
+ report: {
4477
+ /** Тип отчёта, snake_case — тот же, что в пути и в ?type= */
4478
+ type: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics"
4479
+ generatedAt: string
4480
+ /** Начало периода (включительно), ISO-8601 UTC. У balance_position from = to = момент позиции */
4481
+ from: string
4482
+ /** Конец периода (НЕ включительно), ISO-8601 UTC */
4483
+ to: string
4484
+ /** IANA-зона, в которой нарезан timeline. У balance_position всегда UTC */
4485
+ timezone: string
4486
+ }
3913
4487
  categoryKey: string
4488
+ /** Операции с активным назначением этой категории */
3914
4489
  operationsCount: number
4490
+ /** null — metricField не задан */
3915
4491
  metric: {
3916
4492
  fieldKey: string
3917
- agg: string
4493
+ agg: "sum" | "avg" | "min" | "max" | "median" | "count"
3918
4494
  /** null, когда метрика деноминирована — см. byCurrency */
3919
4495
  value: string | null
3920
4496
  byCurrency: {
3921
4497
  [key: string]: unknown
3922
4498
  } | null
3923
4499
  } | null
4500
+ /** null — groupByField не задан */
3924
4501
  groups: Array<{
4502
+ /** Значение поля groupByField; null — у назначения поле не заполнено */
3925
4503
  key: string | null
4504
+ /** Число НАЗНАЧЕНИЙ категории в группе, а не операций: у одной операции их может быть несколько, поэтому сумма по группам бывает больше operationsCount отчёта */
3926
4505
  operationsCount: number
3927
4506
  metric: string | null
3928
4507
  metricByCurrency: {
@@ -3931,6 +4510,80 @@ export interface CategoryAnalytics {
3931
4510
  }> | null
3932
4511
  }
3933
4512
 
4513
+ export interface BalancesAsOfAccount {
4514
+ accountId: string
4515
+ /** available / reserved — счета адресов; external / adjustment — счета уровня Vault'а (без адреса): встречная сторона двойной записи */
4516
+ purpose: "available" | "reserved" | "external" | "adjustment"
4517
+ tokenId: string
4518
+ /** null у vault-level счетов */
4519
+ addressId: string | null
4520
+ /** Raw smallest units со знаком (vault-level счета бывают отрицательными) */
4521
+ balance: string
4522
+ /** То же в единицах актива. Если токена счёта нет в реестре — raw, как в balance */
4523
+ balanceDecimal: string
4524
+ }
4525
+
4526
+ export interface BalancesAsOf {
4527
+ /** Момент среза: параметр ?at=, приведённый к ISO-8601 в UTC (не буквальное эхо) */
4528
+ asOf: string
4529
+ /** По строке на токен; складываются только available и reserved. Пустой массив — движений до момента среза не было (или ?purpose= выбрал external/adjustment): это «нет данных», а не «ноль» */
4530
+ totals: BalancesAsOfTotal[]
4531
+ /** Счета, по которым были движения до момента среза. ОТСУТСТВУЕТ при ?summary=true. Участник видит только счета адресов из своего scope; vault-level счета (external/adjustment) — только admin */
4532
+ accounts?: BalancesAsOfAccount[]
4533
+ }
4534
+
4535
+ export interface BalancesAsOfTotal {
4536
+ token: {
4537
+ code: string
4538
+ symbol: string
4539
+ decimals: number
4540
+ /** Network.slug */
4541
+ network: string
4542
+ }
4543
+ tokenId: string
4544
+ /** DECIMAL (не raw!) — дубль availableDecimal; у GET /balances это поле raw */
4545
+ available: string
4546
+ availableDecimal: string
4547
+ /** DECIMAL (не raw!) — дубль reservedDecimal; у GET /balances это поле raw */
4548
+ reserved: string
4549
+ reservedDecimal: string
4550
+ /** available + reserved, decimal: резерв — ещё не расход, пока перевод не подтверждён */
4551
+ ownedTotal: string
4552
+ }
4553
+
4554
+ export interface CreateReportExportBody {
4555
+ /** json — canonical JSON результата; csv — плоский реестр операций. xlsx и pdf схема принимает, а хендлер отклоняет: 400 REPORT_UNSUPPORTED_FORMAT */
4556
+ format: "json" | "csv" | "xlsx" | "pdf"
4557
+ /** Зарезервировано: сейчас не влияет на файл */
4558
+ locale?: string
4559
+ /** Зарезервировано: сейчас не влияет на файл */
4560
+ timezone?: string
4561
+ /** Зарезервировано: сейчас не влияет на файл (csv — всегда реестр, json — всегда результат) */
4562
+ includeOperationRegister?: boolean
4563
+ }
4564
+
4565
+ export interface CreateReportSnapshotBody {
4566
+ reportType: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics"
4567
+ /** Те же параметры, что уходили в живой отчёт query-строкой (from, to, timezone, фильтры, includeShared, цель отчёта). Сохраняются в снапшоте как есть — под именем `parametersJson` */
4568
+ parameters: {
4569
+ [key: string]: unknown
4570
+ }
4571
+ }
4572
+
4573
+ export interface CreateReportTemplateBody {
4574
+ /** length: 1..120 */
4575
+ name: string
4576
+ /** length: 0..500 */
4577
+ description?: string
4578
+ reportType: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics"
4579
+ /** Параметры отчёта, как в query живого отчёта. Вместо from/to можно задать relativePeriod {unit: day|week|month, offset} — даты вычислятся при запуске. В ответе лежат под именем `parametersJson` */
4580
+ parameters: {
4581
+ [key: string]: unknown
4582
+ }
4583
+ /** true — только admin */
4584
+ isShared?: boolean
4585
+ }
4586
+
3934
4587
  export interface DrilldownResponse {
3935
4588
  /** Операции с полями и categoryAssignments (sensitive-значения отредактированы по правам: значение null + ключ в redactedFields) */
3936
4589
  items: Array<{
@@ -3946,94 +4599,251 @@ export interface DrilldownResponse {
3946
4599
  }
3947
4600
  }
3948
4601
 
4602
+ export interface LedgerEntriesPage {
4603
+ items: LedgerEntry[]
4604
+ nextCursor: string | null
4605
+ }
4606
+
3949
4607
  export interface LedgerEntry {
3950
4608
  id: string
3951
4609
  vaultId: string
3952
- /** deposit | transfer | adjustment … */
4610
+ /** topup | transfer_reserve | transfer_confirm | transfer_refund | adjustment | genesis. Это НЕ хранимый kind: три вида переименованы на выходе (reserve → transfer_reserve, confirm → transfer_confirm, release → transfer_refund), остальные отдаются как есть. Фильтр ?entryType= понимает оба написания */
3953
4611
  entryType: string
3954
- reference: string | null
3955
- description: string | null
3956
- operationType: string | null
4612
+ /** Документ-источник; null у корректировок и genesis */
4613
+ operationType: "transfer" | "deposit" | null
4614
+ /** id перевода или депозита — по operationType */
3957
4615
  operationId: string | null
3958
- token: {
3959
- [key: string]: unknown
3960
- } | null
3961
- effectiveAt: string
4616
+ /** Время записи в журнал. По нему идут сортировка, фильтр from/to и балансы as-of */
3962
4617
  postedAt: string
4618
+ /** Экономическое время события (для on-chain — время блока) */
4619
+ effectiveAt: string
4620
+ /** Ключ идемпотентности проводки (sourceKey), уникален: "topup:{eventId}", "reserve:{transferId}", "confirm:{transferId}", "release:{transferId}" */
4621
+ reference: string
4622
+ /** Примечание к проводке (memo) */
4623
+ description: string | null
4624
+ /** Токен проводки — по её ПЕРВОМУ постингу, взятому до фильтрации под права: присутствует, даже если postings у вызывающего пуст */
4625
+ token: LedgerEntryToken | null
4626
+ postings: LedgerPosting[]
4627
+ /** То же значение, что postedAt */
3963
4628
  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
- }>
4629
+ }
4630
+
4631
+ export interface LedgerEntryToken {
4632
+ id: string
4633
+ code: string
4634
+ symbol: string
4635
+ decimals: number
4636
+ /** Network.slug */
4637
+ network: string
4638
+ /** null у нативного актива сети */
4639
+ contractAddress: string | null
4640
+ }
4641
+
4642
+ export interface LedgerPosting {
4643
+ id: string
4644
+ accountId: string
4645
+ /** available / reserved — счета адресов; external / adjustment — счета уровня Vault'а (без адреса): встречная сторона двойной записи */
4646
+ accountPurpose: "available" | "reserved" | "external" | "adjustment"
4647
+ /** address — счёт адреса; vault — счёт уровня Vault'а (external/adjustment) */
4648
+ ownerType: "address" | "vault"
4649
+ /** addressId для ownerType=address, иначе vaultId */
4650
+ ownerId: string
4651
+ /** null у vault-level счетов */
4652
+ addressId: string | null
4653
+ /** Raw smallest units со знаком: "2500000" | "-2500000" */
4654
+ amountRawSigned: string
4655
+ /** Та же сумма в единицах актива, со знаком: "-2.5". Если токена постинга нет в реестре (чего при живом FK быть не должно), сюда попадает raw — значение совпадёт с amountRawSigned */
4656
+ amountDecimalSigned: string
4657
+ }
4658
+
4659
+ export interface PatchReportTemplateBody {
4660
+ /** length: 1..120 */
4661
+ name?: string
4662
+ /** length: 0..500 */
4663
+ description?: string
4664
+ reportType?: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics"
4665
+ /** Параметры отчёта, как в query живого отчёта. Вместо from/to можно задать relativePeriod {unit: day|week|month, offset} — даты вычислятся при запуске. В ответе лежат под именем `parametersJson` */
4666
+ parameters?: {
4667
+ [key: string]: unknown
4668
+ }
4669
+ /** true — только admin */
4670
+ isShared?: boolean
4671
+ }
4672
+
4673
+ export interface ReportDrilldownBody {
4674
+ reportType?: string
4675
+ /** @defaultValue {} */
4676
+ filters?: {
4677
+ [key: string]: unknown
4678
+ }
4679
+ /** @defaultValue {} */
4680
+ drilldown?: {
4681
+ [key: string]: unknown
4682
+ }
4683
+ snapshotId?: string | null
4684
+ /**
4685
+ * range: 1..500
4686
+ * @defaultValue 100
4687
+ */
4688
+ limit?: number
4689
+ cursor?: string | null
3977
4690
  }
3978
4691
 
3979
4692
  export interface ReportExport {
3980
4693
  id: string
4694
+ vaultId: string
3981
4695
  snapshotId: string
4696
+ /** json | csv */
3982
4697
  format: string
4698
+ /** queued | processing | completed | failed; экспорт синхронный, поэтому сейчас всегда completed */
3983
4699
  status: string
3984
- filename: string | null
4700
+ requestedById: string
4701
+ requestedAt: string
4702
+ completedAt: string | null
4703
+ /** Задел под R2; сейчас всегда null */
4704
+ storageKey: string | null
3985
4705
  mimeType: string | null
4706
+ filename: string | null
4707
+ /** Длина содержимого в СИМВОЛАХ, а не в байтах: для не-ASCII текста (кириллица в подписях адресов) файл на диске будет больше */
3986
4708
  sizeBytes: number | null
4709
+ /** sha256 содержимого файла */
3987
4710
  fileHash: string | null
3988
- requestedById: string
3989
- completedAt: string | null
4711
+ errorCode: string | null
4712
+ errorMessage: string | null
3990
4713
  }
3991
4714
 
3992
- export interface ReportSnapshot {
4715
+ export interface ReportSnapshotDetail {
3993
4716
  id: string
3994
- vaultId: string
3995
4717
  /** snake_case, как и везде */
3996
4718
  reportType: string
3997
- status: string
3998
- schemaVersion: string | null
4719
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
4720
+ schemaVersion: string
4721
+ /** Тело `parameters` запроса создания, как есть */
3999
4722
  parametersJson: {
4000
4723
  [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
4724
+ }
4725
+ generatedById: string
4726
+ generatedAt: string
4727
+ /** Срез данных: операции новее в снапшот не вошли. Совпадает с generatedAt */
4728
+ dataCutoffAt: string
4729
+ /** Число операций в манифесте; 0, если манифеста нет — у отчётов без пооперационной выборки (balance_position, approval_activity) */
4730
+ rowCount: number
4731
+ /** sha256 canonical JSON результата — доказательство, что содержимое не подменили */
4732
+ resultHash: string
4733
+ /** generating | completed | failed; создание синхронное, поэтому сейчас всегда completed */
4734
+ status: string
4735
+ createdAt: string
4736
+ vaultId: string
4737
+ /** sha256 canonical JSON параметров — одинаковый запрос даёт одинаковый хеш */
4738
+ normalizedParametersHash: string
4739
+ /** Задел; сейчас всегда null */
4016
4740
  dataCutoffBlock: number | null
4741
+ /** current | historical — из параметра counterpartyClassification, по умолчанию historical */
4017
4742
  counterpartyClassificationMode: string | null
4743
+ /** current | historical — из параметра folderClassification, по умолчанию historical */
4018
4744
  folderClassificationMode: string | null
4745
+ /** Задел под R2; сейчас всегда null */
4746
+ resultStorageKey: string | null
4747
+ /** Манифест: ключи операций, вошедших в снапшот (по нему строится drill-down по снапшоту). null — у отчёта нет пооперационной выборки или манифест собрать не удалось */
4748
+ includedOperationKeysJson: string[] | null
4749
+ /** Задел под R2; сейчас всегда null */
4750
+ includedOperationKeysStorageKey: string | null
4019
4751
  errorCode: string | null
4020
4752
  errorMessage: string | null
4753
+ /** В ответах всегда null: скрытый (soft-delete) снапшот не отдаётся вовсе — 404 */
4754
+ deletedAt: string | null
4755
+ /** Результат отчёта, зафиксированный при создании. В ответе создания то же самое называется `result`. null возможен только у снапшота, который не достроился (status ≠ completed) */
4756
+ resultJson: {
4757
+ [key: string]: unknown
4758
+ } | null
4759
+ }
4760
+
4761
+ export interface ReportSnapshotListItem {
4762
+ id: string
4763
+ /** snake_case, как и везде */
4764
+ reportType: string
4765
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
4766
+ schemaVersion: string
4767
+ /** Тело `parameters` запроса создания, как есть */
4768
+ parametersJson: {
4769
+ [key: string]: unknown
4770
+ }
4771
+ generatedById: string
4772
+ generatedAt: string
4773
+ /** Срез данных: операции новее в снапшот не вошли. Совпадает с generatedAt */
4774
+ dataCutoffAt: string
4775
+ /** Число операций в манифесте; 0, если манифеста нет — у отчётов без пооперационной выборки (balance_position, approval_activity) */
4776
+ rowCount: number
4777
+ /** sha256 canonical JSON результата — доказательство, что содержимое не подменили */
4778
+ resultHash: string
4779
+ /** generating | completed | failed; создание синхронное, поэтому сейчас всегда completed */
4780
+ status: string
4781
+ createdAt: string
4782
+ }
4783
+
4784
+ export interface ReportSnapshotList {
4785
+ items: ReportSnapshotListItem[]
4786
+ }
4787
+
4788
+ export interface ReportSnapshot {
4789
+ id: string
4790
+ /** snake_case, как и везде */
4791
+ reportType: string
4792
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
4793
+ schemaVersion: string
4794
+ /** Тело `parameters` запроса создания, как есть */
4795
+ parametersJson: {
4796
+ [key: string]: unknown
4797
+ }
4021
4798
  generatedById: string
4022
4799
  generatedAt: string
4800
+ /** Срез данных: операции новее в снапшот не вошли. Совпадает с generatedAt */
4801
+ dataCutoffAt: string
4802
+ /** Число операций в манифесте; 0, если манифеста нет — у отчётов без пооперационной выборки (balance_position, approval_activity) */
4803
+ rowCount: number
4804
+ /** sha256 canonical JSON результата — доказательство, что содержимое не подменили */
4805
+ resultHash: string
4806
+ /** generating | completed | failed; создание синхронное, поэтому сейчас всегда completed */
4807
+ status: string
4023
4808
  createdAt: string
4809
+ vaultId: string
4810
+ /** sha256 canonical JSON параметров — одинаковый запрос даёт одинаковый хеш */
4811
+ normalizedParametersHash: string
4812
+ /** Задел; сейчас всегда null */
4813
+ dataCutoffBlock: number | null
4814
+ /** current | historical — из параметра counterpartyClassification, по умолчанию historical */
4815
+ counterpartyClassificationMode: string | null
4816
+ /** current | historical — из параметра folderClassification, по умолчанию historical */
4817
+ folderClassificationMode: string | null
4818
+ /** Задел под R2; сейчас всегда null */
4819
+ resultStorageKey: string | null
4820
+ /** Манифест: ключи операций, вошедших в снапшот (по нему строится drill-down по снапшоту). null — у отчёта нет пооперационной выборки или манифест собрать не удалось */
4821
+ includedOperationKeysJson: string[] | null
4822
+ /** Задел под R2; сейчас всегда null */
4823
+ includedOperationKeysStorageKey: string | null
4824
+ errorCode: string | null
4825
+ errorMessage: string | null
4826
+ /** В ответах всегда null: скрытый (soft-delete) снапшот не отдаётся вовсе — 404 */
4024
4827
  deletedAt: string | null
4828
+ /** Результат отчёта — тот же объект, что вернул бы живой отчёт. ТОЛЬКО в ответе создания; деталь отдаёт его как `resultJson` */
4829
+ result: {
4830
+ [key: string]: unknown
4831
+ }
4025
4832
  }
4026
4833
 
4027
4834
  export interface ReportTemplate {
4028
4835
  id: string
4029
4836
  vaultId: string
4837
+ /** Создатель шаблона */
4838
+ ownerId: string
4030
4839
  name: string
4840
+ description: string | null
4031
4841
  reportType: string
4842
+ /** Тело `parameters` запроса создания, как есть (включая relativePeriod) */
4032
4843
  parametersJson: {
4033
4844
  [key: string]: unknown
4034
- } | null
4845
+ }
4035
4846
  isShared: boolean
4036
- createdById: string
4037
4847
  createdAt: string
4038
- updatedAt?: string
4848
+ updatedAt: string
4039
4849
  }