@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,11 +1,11 @@
1
- /** Единая форма ошибки для ВСЕХ эндпоинтов. Приводится слоем middleware/errorEnvelope независимо от того, как ошибку собрал хендлер */
1
+ /** Единая форма ошибки. Приводится слоем middleware/errorEnvelope независимо от того, как ошибку собрал хендлер. Слой узнаёт ошибку по ключу `error` в корне тела; ответ со статусом ≥400 без этого ключа он не трогает — так отвечает пакетная выдача доступа, когда отклонены все получатели: 400 с телом `{results}` */
2
2
  export interface ApiErrorEnvelope {
3
3
  error: {
4
4
  /** Машиночитаемый код. Осмысленный там, где его задаёт хендлер (INSUFFICIENT_BALANCE, POLICY_DENIED, APPROVAL_UNSATISFIABLE…), иначе выведен из HTTP-статуса (NOT_FOUND, FORBIDDEN…) */
5
5
  code: string;
6
6
  /** Человекочитаемое описание — можно показывать пользователю */
7
7
  message: string;
8
- /** Структурированные подробности, если есть */
8
+ /** Структурированные подробности, если есть (поля валидации, трейс политик). Сюда же слой переносит машиночитаемые ключи, которые хендлер положил рядом с error: `field` — ключ поля категории, не прошедшего валидацию; `stepIndex` — номер отказавшего шага композитной заявки */
9
9
  details?: unknown;
10
10
  };
11
11
  }
@@ -142,6 +142,8 @@ export interface AddressAccessGrant {
142
142
  canCreateAddress: boolean;
143
143
  grantorId: string;
144
144
  status: "active" | "revoked";
145
+ revokedAt: string | null;
146
+ revokedById: string | null;
145
147
  /** format: date-time */
146
148
  createdAt: string;
147
149
  /** format: date-time */
@@ -340,8 +342,17 @@ export interface AddressGroupDetail {
340
342
  createdAt: string;
341
343
  /** format: date-time */
342
344
  updatedAt: string;
343
- /** range: -9007199254740991..9007199254740991 */
345
+ /**
346
+ * Сколько адресов в папке. Приходит в списке и детали
347
+ *
348
+ * range: -9007199254740991..9007199254740991
349
+ */
344
350
  memberCount?: number;
351
+ /** Служебный счётчик ORM — приезжает в списке и детали рядом с memberCount и равен ему. Читайте memberCount: это поле — деталь реализации, объявлено только потому, что фактически отдаётся */
352
+ _count?: {
353
+ /** range: -9007199254740991..9007199254740991 */
354
+ members: number;
355
+ };
345
356
  /** Как у меня доступ к группе */
346
357
  access?: "manager" | "granted" | "admin";
347
358
  /** Открыта ли папка кому-то ещё + сколько ЛЮДЕЙ имеют доступ. Ортогонально полю access: своя расшаренная папка — это access=manager + isShared=true */
@@ -376,8 +387,17 @@ export interface AddressGroup {
376
387
  createdAt: string;
377
388
  /** format: date-time */
378
389
  updatedAt: string;
379
- /** range: -9007199254740991..9007199254740991 */
390
+ /**
391
+ * Сколько адресов в папке. Приходит в списке и детали
392
+ *
393
+ * range: -9007199254740991..9007199254740991
394
+ */
380
395
  memberCount?: number;
396
+ /** Служебный счётчик ORM — приезжает в списке и детали рядом с memberCount и равен ему. Читайте memberCount: это поле — деталь реализации, объявлено только потому, что фактически отдаётся */
397
+ _count?: {
398
+ /** range: -9007199254740991..9007199254740991 */
399
+ members: number;
400
+ };
381
401
  /** Как у меня доступ к группе */
382
402
  access?: "manager" | "granted" | "admin";
383
403
  /** Открыта ли папка кому-то ещё + сколько ЛЮДЕЙ имеют доступ. Ортогонально полю access: своя расшаренная папка — это access=manager + isShared=true */
@@ -878,7 +898,7 @@ export interface AssignCategoryBody {
878
898
  * Значения полей; обязательные (required) должны быть заполнены
879
899
  * @defaultValue {}
880
900
  */
881
- values: {
901
+ values?: {
882
902
  [key: string]: unknown;
883
903
  };
884
904
  /** length: 0..500 */
@@ -939,7 +959,12 @@ export interface CancelResult {
939
959
  }
940
960
  /** Тело не требуется — инициатор из сессии */
941
961
  export type CancelTransferBody = Record<string, never>;
942
- export type CategoryAccessScope = unknown;
962
+ /**
963
+ * Что категория открывает по умолчанию, без гранта (какое именно право — зависит от поля: viewScope, assignScope, editScope). owner_only — ничего сверх доступа через адрес операции (свои операции, совместный доступ) и грантов категории; vault_members — право есть у каждого участника Vault'а.
964
+ *
965
+ * В ответах приходят только эти два значения. В ЗАПРОСЕ дополнительно принимается устаревший псевдоним "restricted": записывается как "owner_only" (вёл себя так же; миграция 0037 убрала его из данных). В перечень значений он не входит намеренно: описание общее для запроса и ответа, а в ответе его не бывает. Новому коду слать его незачем
966
+ */
967
+ export type CategoryAccessScope = "owner_only" | "vault_members";
943
968
  export interface CompositeApplied {
944
969
  /** Все шаги allow/bypass — цепочка применена сразу, без заявки */
945
970
  applied: true;
@@ -1729,6 +1754,8 @@ export type EvaluationResult = {
1729
1754
  export interface PolicyTrace {
1730
1755
  policyId: string;
1731
1756
  policyCode: string;
1757
+ /** Редакция политики, которая оценивалась (Ф4) — та же, что попадёт в `decisionPolicyVersionId` перевода. null — у политики ещё нет версий */
1758
+ policyVersionId: string | null;
1732
1759
  policyName: string;
1733
1760
  effect: string;
1734
1761
  applied: boolean;
@@ -2399,20 +2426,36 @@ export interface Participant {
2399
2426
  userId: string;
2400
2427
  user: UserCard | null;
2401
2428
  role: ShareRole;
2402
- /**
2403
- * ОТКУДА доступ — и, следовательно, что делать, чтобы его убрать:
2404
- * - {kind:"direct", grantId} — прямой грант, отзывается точечно
2405
- * - {kind:"user_group", groupId, groupName} — грант выдан ГРУППЕ пользователей: точечно не отзывается, нужно вывести человека из группы
2406
- * - {kind:"address_group", groupId, groupName} — доступ через папку, куда входит адрес
2407
- *
2408
- * Источников может быть НЕСКОЛЬКО сразу; итоговая роль — максимум прав
2409
- */
2410
- via: Array<{
2411
- [key: string]: unknown;
2412
- }>;
2429
+ /** Источников может быть НЕСКОЛЬКО сразу; итоговая роль — максимум прав */
2430
+ via: ParticipantVia[];
2413
2431
  /** true — есть прямой грант, участника можно убрать одной операцией */
2414
2432
  removable: boolean;
2415
2433
  }
2434
+ /**
2435
+ * ОТКУДА доступ — и, следовательно, что делать, чтобы его убрать:
2436
+ * - direct — прямой грант, отзывается точечно
2437
+ * - user_group — грант выдан ГРУППЕ пользователей: точечно не отзывается, нужно вывести человека из группы
2438
+ * - address_group — доступ через папку, куда входит адрес (у участников самой папки не встречается)
2439
+ */
2440
+ export type ParticipantVia = {
2441
+ kind: "direct";
2442
+ /** Грант на сам объект — отзывается точечно */
2443
+ grantId: string;
2444
+ } | {
2445
+ kind: "user_group";
2446
+ /** Грант, выданный группе пользователей */
2447
+ grantId: string;
2448
+ groupId: string;
2449
+ /** Имя группы — чтобы объяснить «доступ у вас от группы Казначеи» */
2450
+ groupName: string;
2451
+ } | {
2452
+ kind: "address_group";
2453
+ /** Грант, выданный на папку */
2454
+ grantId: string;
2455
+ /** Папка, в которой лежит адрес */
2456
+ groupId: string;
2457
+ groupName: string;
2458
+ };
2416
2459
  /** Кто имеет доступ к объекту — списком ЛЮДЕЙ, а не грантов. Админы не включаются: их доступ надзорный, есть ко всему сразу и совместным доступом не является */
2417
2460
  export interface ParticipantsResponse {
2418
2461
  /** Владелец объекта: у адреса — создатель, у папки — менеджер. Не участник */
@@ -2634,6 +2677,7 @@ export interface PendingApproval {
2634
2677
  }
2635
2678
  export interface PolicyApprover {
2636
2679
  id: string;
2680
+ policyId: string;
2637
2681
  /** "user" | "group" */
2638
2682
  kind: string;
2639
2683
  userId: string | null;
@@ -2667,6 +2711,14 @@ export interface PolicyDetail {
2667
2711
  createdAt: string;
2668
2712
  /** format: date-time */
2669
2713
  updatedAt: string;
2714
+ /**
2715
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
2716
+ *
2717
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
2718
+ *
2719
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
2720
+ */
2721
+ currentVersionId: string | null;
2670
2722
  /** LEGACY: движок их не читает, решает conditionJson */
2671
2723
  rules: Rule[];
2672
2724
  /** Явные одобряющие (для allow-политики с approval{}) */
@@ -2809,6 +2861,14 @@ export interface Policy {
2809
2861
  createdAt: string;
2810
2862
  /** format: date-time */
2811
2863
  updatedAt: string;
2864
+ /**
2865
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
2866
+ *
2867
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
2868
+ *
2869
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
2870
+ */
2871
+ currentVersionId: string | null;
2812
2872
  }
2813
2873
  export interface PolicyTableItem {
2814
2874
  /** cuid */
@@ -2826,10 +2886,6 @@ export interface PolicyTableItem {
2826
2886
  adminBypass: boolean;
2827
2887
  allowInitiatorApproval: boolean;
2828
2888
  enabled: boolean;
2829
- /** Условие применимости — булево дерево. ЭТО ЧИТАЕТ ДВИЖОК. null — политика применима всегда. Legacy-таблица rules движком не используется и удаляется в Ф5 */
2830
- conditionJson: PolicyCondition | null;
2831
- /** Требование подписи. null — политика разрешает без одобрения */
2832
- approvalJson: PolicyApprovalInput | null;
2833
2889
  /** format: date-time */
2834
2890
  createdAt: string;
2835
2891
  /** format: date-time */
@@ -2888,6 +2944,14 @@ export interface PolicyWithRules {
2888
2944
  createdAt: string;
2889
2945
  /** format: date-time */
2890
2946
  updatedAt: string;
2947
+ /**
2948
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
2949
+ *
2950
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
2951
+ *
2952
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
2953
+ */
2954
+ currentVersionId: string | null;
2891
2955
  /** LEGACY: движок их не читает, решает conditionJson */
2892
2956
  rules: Rule[];
2893
2957
  /** Явные одобряющие (для allow-политики с approval{}) */
@@ -2997,20 +3061,39 @@ export interface SeatCreated {
2997
3061
  /** format: date-time */
2998
3062
  expiresAt: string;
2999
3063
  }
3064
+ export interface ShareBatchAllRejected {
3065
+ /** items: 1..∞ */
3066
+ results: ShareBatchRejectedItem[];
3067
+ }
3068
+ export interface ShareBatchRejectedItem {
3069
+ subjectType: "user" | "group";
3070
+ subjectUserId: string | null;
3071
+ subjectGroupId: string | null;
3072
+ ok: false;
3073
+ outcome: null;
3074
+ grant: null;
3075
+ error: {
3076
+ /** SELF_SHARE — себе; OWNER_SUBJECT — владельцу адреса; NOT_MEMBER — не участник Vault'а; NOT_VISIBLE — получатель вам не виден (нет гранта видимости); GROUP_NOT_FOUND — группы нет в этом Vault'е */
3077
+ code: string;
3078
+ /** Сейчас дублирует code — человеческий текст собирает клиент */
3079
+ message: string;
3080
+ };
3081
+ }
3082
+ export interface ShareBatchGrantedItem {
3083
+ subjectType: "user" | "group";
3084
+ subjectUserId: string | null;
3085
+ subjectGroupId: string | null;
3086
+ ok: true;
3087
+ /** created — новый грант; updated — у действующего гранта сменили права; reactivated — снят отзыв с ранее отозванного */
3088
+ outcome: "created" | "updated" | "reactivated";
3089
+ grant: AddressAccessGrant;
3090
+ error: null;
3091
+ }
3092
+ export type ShareBatchItem = ShareBatchGrantedItem | ShareBatchRejectedItem;
3000
3093
  export interface ShareBatchResult {
3001
- results: Array<{
3002
- subjectType: "user" | "group";
3003
- subjectUserId: string | null;
3004
- subjectGroupId: string | null;
3005
- ok: boolean;
3006
- outcome: "created" | "updated" | "reactivated" | null;
3007
- grant: AddressAccessGrant | null;
3008
- error: {
3009
- code: string;
3010
- message: string;
3011
- } | null;
3012
- }>;
3094
+ results: ShareBatchItem[];
3013
3095
  }
3096
+ export type ShareGrantCreated = AddressAccessGrant | ShareBatchResult;
3014
3097
  export interface Token {
3015
3098
  /** cuid */
3016
3099
  id: string;
@@ -3121,6 +3204,16 @@ export interface TransactionDetail {
3121
3204
  amlScore: number | null;
3122
3205
  /** Когда провайдер сделал проверку */
3123
3206
  amlCheckedAt: string | null;
3207
+ /** USD-оценка перевода: amountDecimal × usdRate, decimal-СТРОКА. Считается один раз при создании и live-курсом не пересчитывается; null — курс был недоступен */
3208
+ usdValue: string | null;
3209
+ /** Редакция политики, принявшая решение (PolicyVersion.id, Ф4). ЕДИНСТВЕННЫЙ способ связать перевод с правилами, которые действовали в момент решения: `decisionPolicyId` ведёт на политику, а её с тех пор могли переписать. Снимок — GET /policies/{id}/versions. null — решение принято до введения версий */
3210
+ decisionPolicyVersionId: string | null;
3211
+ /** Группы контрагентов получателя НА МОМЕНТ РЕШЕНИЯ (id групп). Версия политики хранит id групп белого списка, но не их состав — без этого снимка нельзя объяснить, почему платёж прошёл, после того как контрагента из группы убрали. null — получатель не был контрагентом */
3212
+ decisionCounterpartyGroupsJson: string[] | null;
3213
+ /** Контрагент, сопоставленный по toAddress при создании. Дублирует `counterparty.id` — отдаётся как колонка перевода */
3214
+ counterpartyId: string | null;
3215
+ /** Клиентский ключ попытки, с которым перевод создан (см. тело POST /transfers). null — ключ не передавали */
3216
+ idempotencyKey: string | null;
3124
3217
  amountDecimal: string;
3125
3218
  assetSymbol: string;
3126
3219
  }
@@ -3211,7 +3304,7 @@ export interface UserProfile {
3211
3304
  color: string | null;
3212
3305
  /** Emoji-иконка */
3213
3306
  emoji: string | null;
3214
- /** true — аккаунт создан по пригласительной ссылке и настоящая почта ещё НЕ указана (в email лежит технический адрес). Клиент обязан попросить адрес: пока его нет, человеку некуда прислать magic-link, и при истечении сессии он потеряет доступ. Привязка — POST /me/email */
3307
+ /** true — аккаунт создан по пригласительной ссылке и настоящая почта ещё НЕ указана (в email лежит технический адрес). Клиент обязан попросить адрес: пока его нет, человеку некуда прислать magic-link, и при истечении сессии он потеряет доступ. Привязка — POST /me/profile/email. Приходит и в GET, и в ответе PATCH */
3215
3308
  needsEmail: boolean;
3216
3309
  /** format: date-time */
3217
3310
  createdAt: string;
@@ -3297,6 +3390,7 @@ export interface VaultPoliciesPage {
3297
3390
  items: Array<{
3298
3391
  /** cuid */
3299
3392
  id: string;
3393
+ groupId: string;
3300
3394
  code: string;
3301
3395
  name: string;
3302
3396
  description: string | null;
@@ -3318,11 +3412,17 @@ export interface VaultPoliciesPage {
3318
3412
  createdAt: string;
3319
3413
  /** format: date-time */
3320
3414
  updatedAt: string;
3415
+ /**
3416
+ * Текущая редакция политики (PolicyVersion.id, Ф4). На редакцию, а не на политику, ссылается решение: `decisionPolicyVersionId` у перевода и `policyVersionId` в ответе /evaluate.
3417
+ *
3418
+ * POST /policies отдаёт id ПЕРВОЙ редакции, PATCH — редакции, снятой этой правкой. Раньше POST всегда отвечал null, хотя версия к этому моменту уже была снята (ответ собирался из строки, прочитанной до снимка), и за значением приходилось идти вторым запросом.
3419
+ *
3420
+ * null — редакции нет или её id не удалось прочитать: у POST значение дочитывается отдельным запросом, и его сбой создание не отменяет. Получив null, спросите GET /policies/{id}
3421
+ */
3422
+ currentVersionId: string | null;
3321
3423
  _count: {
3322
3424
  /** range: -9007199254740991..9007199254740991 */
3323
3425
  rules: number;
3324
- /** range: -9007199254740991..9007199254740991 */
3325
- limits?: number;
3326
3426
  };
3327
3427
  /** Owner-группа политики */
3328
3428
  group: GroupRef;
@@ -3369,8 +3469,12 @@ export interface VisibilityGrant {
3369
3469
  } | null;
3370
3470
  status: string;
3371
3471
  grantorId: string;
3472
+ revokedAt: string | null;
3473
+ revokedById: string | null;
3372
3474
  /** format: date-time */
3373
3475
  createdAt: string;
3476
+ /** format: date-time */
3477
+ updatedAt: string;
3374
3478
  }
3375
3479
  export interface VoteBody {
3376
3480
  /** reject = вето: перевод сразу rejected + release средств */
@@ -3440,35 +3544,106 @@ export type WebhookResult = {
3440
3544
  duplicate: true;
3441
3545
  eventId: string;
3442
3546
  };
3443
- export interface ActivityReport {
3547
+ export interface AccountStatementReport {
3444
3548
  report: ReportMeta;
3445
- summary?: ReportSummary;
3446
- byAsset?: ByAssetRow[];
3447
- byCounterparty?: ByCounterpartyRow[];
3448
- byOwnAddress?: ByOwnAddressRow[];
3449
- byStatus?: ByStatusRow[];
3450
- byCategory?: ByCategoryRow[];
3451
- byAddressOwner?: ByAddressOwnerRow[];
3452
- timeline?: TimelinePoint[];
3453
- approvals?: ReportApprovals;
3454
- observations?: Observation[];
3455
- dataQuality?: DataQuality;
3456
- comparison?: Comparison | null;
3549
+ balancesByAsset: StatementTokenRow[];
3550
+ timeline: Array<{
3551
+ /** Начало интервала (как periodStart у остальных) */
3552
+ period: string;
3553
+ /** Задел; сейчас всегда пустая строка */
3554
+ opening: string;
3555
+ /** Приток ТЕКСТОМ для печати: "1.5 USDT; 20 TRX" — сумма с символом, активы через «; ». Числом не разбирать. Перечислен КАЖДЫЙ актив, по которому в интервале были операции, даже если притока по нему не было: тогда он приходит как "0 USDT", а не пропадает */
3556
+ incoming: string;
3557
+ /** Отток, в том же текстовом виде и с теми же нулевыми позициями */
3558
+ outgoing: string;
3559
+ /** Задел; сейчас всегда "0" */
3560
+ adjustments: string;
3561
+ /** Задел; сейчас всегда пустая строка */
3562
+ closing: string;
3563
+ }>;
3564
+ dataQuality: DataQuality;
3457
3565
  }
3458
3566
  export interface ReportMeta {
3459
- id?: string | null;
3460
- /** snake_case: transaction_activity и т.д. */
3461
- type: string;
3462
- from: string | null;
3463
- to: string | null;
3464
- timezone: string | null;
3465
- dateBasis?: string | null;
3466
- baseCurrency?: string | null;
3467
- classificationMode?: string | null;
3468
- dataCutoff?: string | null;
3469
- schemaVersion?: string | number | null;
3567
+ /** Задел под сохранённый отчёт. У живого отчёта всегда null */
3568
+ id: string | null;
3569
+ /** Тип отчёта, snake_case — тот же, что в пути и в ?type= */
3570
+ type: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics";
3470
3571
  generatedAt: string;
3471
- generatedById?: string | null;
3572
+ /** Кто запросил отчёт */
3573
+ generatedById: string;
3574
+ /** Начало периода (включительно), ISO-8601 UTC. У balance_position from = to = момент позиции */
3575
+ from: string;
3576
+ /** Конец периода (НЕ включительно), ISO-8601 UTC */
3577
+ to: string;
3578
+ /** IANA-зона, в которой нарезан timeline. У balance_position всегда UTC */
3579
+ timezone: string;
3580
+ /** По какой дате операция отнесена к периоду. Если параметр не прислан: blockchainAt у counterparty_activity и counterparty_group_activity, createdAt у остальных */
3581
+ dateBasis: "createdAt" | "blockchainAt" | "confirmedAt";
3582
+ baseCurrency: "USD";
3583
+ /** Значение параметра counterpartyClassification (по умолчанию historical). У balance_position всегда current */
3584
+ classificationMode: "current" | "historical";
3585
+ dataCutoff: {
3586
+ /** Операции новее в отчёт не вошли */
3587
+ timestamp: string;
3588
+ /** Задел; сейчас всегда null */
3589
+ blockNumber: number | null;
3590
+ };
3591
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
3592
+ schemaVersion: string;
3593
+ }
3594
+ export interface StatementTokenRow {
3595
+ tokenId: string;
3596
+ assetCode: string;
3597
+ assetSymbol: string;
3598
+ /** Network.slug */
3599
+ network: string;
3600
+ openingAvailable: string;
3601
+ openingReserved: string;
3602
+ openingTotal: string;
3603
+ incomingExternal: string;
3604
+ outgoingExternal: string;
3605
+ incomingInternal: string;
3606
+ outgoingInternal: string;
3607
+ /** Ручные корректировки ledger (kind=adjustment) за период */
3608
+ adjustments: string;
3609
+ closingAvailable: string;
3610
+ closingReserved: string;
3611
+ closingTotal: string;
3612
+ /** (opening + движения + adjustments) − closing из ledger. Ожидается "0"; ненулевое значение — операции и ledger разошлись, и оно же считается в dataQuality.ledgerReconciliationDifferences */
3613
+ reconciliationDifference: string;
3614
+ }
3615
+ export interface DataQuality {
3616
+ totalOperations: number;
3617
+ unmatchedOperations: number;
3618
+ retroactivelyMatchedOperations: number;
3619
+ operationsWithoutUsdRate: number;
3620
+ operationsWithoutSelectedDate: number;
3621
+ operationsWithoutBlockchainTime: number;
3622
+ operationsWithoutPaymentPurpose: number;
3623
+ operationsWithoutBusinessReference: number;
3624
+ uncategorizedOperations: number;
3625
+ conflictingCounterpartyClassifications: number;
3626
+ invalidOperationRelations: number;
3627
+ missingLedgerEntries: number;
3628
+ ledgerReconciliationDifferences: number;
3629
+ }
3630
+ /** УСТАРЕЛО: используйте схему конкретного отчёта (TransactionActivityReport, AssetMovementReport, …) или AnyReport. Имя сохранено для совместимости сгенерированных клиентов: это объединение девяти отчётов, кроме category_analytics */
3631
+ export type ActivityReport = TransactionActivityReport | CounterpartyActivityReport | CounterpartyGroupActivityReport | AssetMovementReport | AddressActivityReport | AccountStatementReport | OperationsExceptionsReport | ApprovalActivityReport | BalancePositionReport;
3632
+ export interface TransactionActivityReport {
3633
+ report: ReportMeta;
3634
+ summary: ReportSummary;
3635
+ byAsset: ByAssetRow[];
3636
+ byCounterparty: ByCounterpartyRow[];
3637
+ byOwnAddress: ByOwnAddressRow[];
3638
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
3639
+ byAddressOwner?: ByAddressOwnerRow[];
3640
+ byStatus: ByStatusRow[];
3641
+ byCategory: ByCategoryRow[];
3642
+ timeline: TimelinePoint[];
3643
+ approvals: ReportApprovals;
3644
+ comparison: Comparison;
3645
+ observations: Observation[];
3646
+ dataQuality: DataQuality;
3472
3647
  }
3473
3648
  export interface ReportSummary {
3474
3649
  operationsCount: number;
@@ -3498,30 +3673,37 @@ export interface ReportSummary {
3498
3673
  minimumAmountByAsset: AssetAmount[];
3499
3674
  /** @defaultValue [] */
3500
3675
  maximumAmountByAsset: AssetAmount[];
3501
- confirmedCount?: number;
3502
- pendingCount?: number;
3503
- awaitingApprovalCount?: number;
3504
- rejectedCount?: number;
3505
- cancelledCount?: number;
3506
- failedCount?: number;
3507
- activeDays?: number | null;
3508
- firstOperationAt?: string | null;
3509
- lastOperationAt?: string | null;
3676
+ confirmedCount: number;
3677
+ pendingCount: number;
3678
+ awaitingApprovalCount: number;
3679
+ rejectedCount: number;
3680
+ cancelledCount: number;
3681
+ failedCount: number;
3682
+ /** Число календарных дней (в timezone отчёта) с хотя бы одной операцией */
3683
+ activeDays: number;
3684
+ /** createdAt самой ранней операции выборки; null — выборка пуста */
3685
+ firstOperationAt: string | null;
3686
+ /** createdAt самой поздней операции выборки; null — выборка пуста */
3687
+ lastOperationAt: string | null;
3510
3688
  }
3511
3689
  export interface AssetAmount {
3512
- tokenId: string | null;
3513
- assetCode: string | null;
3690
+ tokenId: string;
3691
+ /** Token.code из реестра */
3692
+ assetCode: string;
3514
3693
  assetSymbol: string;
3515
- network: string | null;
3516
- contractAddress?: string | null;
3517
- /** raw smallest units, строкой */
3694
+ /** Network.slug */
3695
+ network: string;
3696
+ /** DECIMAL-строка в единицах актива ("1.5"), НЕ raw smallest units; хвостовые нули убраны. Со знаком только у netFlow* ("-12.5" — отток больше притока), у остальных блоков неотрицательна */
3518
3697
  amount: string;
3519
3698
  }
3520
3699
  export interface ByAssetRow {
3521
- tokenId: string | null;
3522
- assetCode: string | null;
3700
+ tokenId: string;
3701
+ /** Token.code из реестра */
3702
+ assetCode: string;
3523
3703
  assetSymbol: string;
3524
- network: string | null;
3704
+ /** Network.slug */
3705
+ network: string;
3706
+ /** null у нативного актива сети */
3525
3707
  contractAddress: string | null;
3526
3708
  incoming: string;
3527
3709
  outgoing: string;
@@ -3544,8 +3726,11 @@ export interface ByAssetRow {
3544
3726
  maximumAmount: string | null;
3545
3727
  }
3546
3728
  export interface ByCounterpartyRow {
3729
+ /** null — операции без сопоставленного контрагента (одна строка на все) */
3547
3730
  counterpartyId: string | null;
3731
+ /** Имя контрагента. У строки без контрагента (counterpartyId = null) сюда приходит английская заглушка "unmatched counterparty" — она не локализуется, поэтому различать такую строку надо по counterpartyId */
3548
3732
  name: string | null;
3733
+ /** Свободная категория контрагента (exchange, client…); null — не задана */
3549
3734
  kind: string | null;
3550
3735
  /** @defaultValue [] */
3551
3736
  incomingByAsset: AssetAmount[];
@@ -3566,7 +3751,7 @@ export interface ByOwnAddressRow {
3566
3751
  [key: string]: unknown;
3567
3752
  } | null;
3568
3753
  /**
3569
- * Папки, в которых адрес состоял (зависит от folderClassification)
3754
+ * Задел под папки адреса. Сейчас ВСЕГДА пустой массив
3570
3755
  * @defaultValue []
3571
3756
  */
3572
3757
  folderSnapshots: unknown[];
@@ -3578,6 +3763,19 @@ export interface ByOwnAddressRow {
3578
3763
  grossTurnoverUsd: string | null;
3579
3764
  operationsCount: number;
3580
3765
  }
3766
+ export interface ByAddressOwnerRow {
3767
+ /** Владелец адресов (создатель) */
3768
+ userId: string;
3769
+ /** Считается от вызывающего: у двух людей один отчёт разложится по-разному */
3770
+ isMe: boolean;
3771
+ /** Сколько адресов этого владельца попало в выборку */
3772
+ addressesCount: number;
3773
+ incomingByAsset: AssetAmount[];
3774
+ outgoingByAsset: AssetAmount[];
3775
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3776
+ grossTurnoverUsd: string | null;
3777
+ operationsCount: number;
3778
+ }
3581
3779
  export interface ByStatusRow {
3582
3780
  status: string;
3583
3781
  count: number;
@@ -3596,16 +3794,6 @@ export interface ByCategoryRow {
3596
3794
  /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3597
3795
  usdValue: string | null;
3598
3796
  }
3599
- export interface ByAddressOwnerRow {
3600
- ownerId: string | null;
3601
- owner?: {
3602
- [key: string]: unknown;
3603
- } | null;
3604
- isMe: boolean;
3605
- operationsCount: number;
3606
- /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3607
- grossTurnoverUsd: string | null;
3608
- }
3609
3797
  export interface TimelinePoint {
3610
3798
  periodStart: string;
3611
3799
  periodEnd: string;
@@ -3633,71 +3821,442 @@ export interface ReportApprovals {
3633
3821
  medianApprovalSeconds: number | null;
3634
3822
  maximumApprovalSeconds: number | null;
3635
3823
  }
3824
+ export interface Comparison {
3825
+ /** none | previous_period | previous_year */
3826
+ mode: string;
3827
+ previousFrom: string | null;
3828
+ previousTo: string | null;
3829
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
3830
+ incomingUsdChangePercent: string | null;
3831
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
3832
+ outgoingUsdChangePercent: string | null;
3833
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
3834
+ grossTurnoverUsdChangePercent: string | null;
3835
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
3836
+ operationsCountChangePercent: string | null;
3837
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — сравнивать не с чем: mode=none, прошлое значение нулевое при ненулевом текущем, либо нет USD-оценки */
3838
+ averageOperationUsdChangePercent: string | null;
3839
+ }
3636
3840
  export interface Observation {
3841
+ /** Машиночитаемый код: COUNTERPARTY_TURNOVER_INCREASED, SINGLE_OPERATION_CONCENTRATION, UNMATCHED_OPERATIONS_PRESENT… */
3637
3842
  code: string;
3638
- severity: string;
3843
+ severity: "info" | "warning";
3639
3844
  title: string;
3640
3845
  description: string;
3641
3846
  metric: string | null;
3642
3847
  currentValue: string | null;
3848
+ /** Задел; сейчас всегда null */
3643
3849
  comparisonValue: string | null;
3644
- changePercent: number | null;
3645
- drilldownFilters?: {
3850
+ /** Изменение к прошлому периоду в процентах — decimal-СТРОКА ("12.5", "-40"), не число. null — наблюдение не про сравнение периодов (концентрация, качество данных, зависшие операции). Не null только у наблюдений о росте или падении оборота и среднего платежа — там это то же значение, что в comparison */
3851
+ changePercent: string | null;
3852
+ /** Готовое уточнение для drill-down, раскрывающее наблюдение до операций; {} — раскрывать нечем */
3853
+ drilldownFilters: {
3646
3854
  [key: string]: unknown;
3647
- } | null;
3855
+ };
3648
3856
  }
3649
- export interface DataQuality {
3650
- totalOperations: number;
3651
- unmatchedOperations: number;
3652
- retroactivelyMatchedOperations: number;
3653
- operationsWithoutUsdRate: number;
3654
- operationsWithoutSelectedDate: number;
3655
- operationsWithoutBlockchainTime: number;
3656
- operationsWithoutPaymentPurpose: number;
3657
- operationsWithoutBusinessReference: number;
3658
- uncategorizedOperations: number;
3659
- conflictingCounterpartyClassifications: number;
3660
- invalidOperationRelations: number;
3661
- missingLedgerEntries: number;
3662
- ledgerReconciliationDifferences: number;
3857
+ export interface CounterpartyActivityReport {
3858
+ report: ReportMeta;
3859
+ /** Карточка контрагента на момент построения отчёта */
3860
+ counterparty: {
3861
+ id: string;
3862
+ name: string;
3863
+ kind: string | null;
3864
+ /** Архивный контрагент отчёт не блокирует — история по нему остаётся */
3865
+ archived: boolean;
3866
+ /** Адресов в справочнике у контрагента */
3867
+ addressesCount: number;
3868
+ /** Группы контрагентов, в которых он состоит СЕЙЧАС */
3869
+ groupIds: string[];
3870
+ };
3871
+ summary: ReportSummary;
3872
+ byAsset: ByAssetRow[];
3873
+ byCounterpartyAddress: ByCounterpartyAddressRow[];
3874
+ byOwnAddress: ByOwnAddressRow[];
3875
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
3876
+ byAddressOwner?: ByAddressOwnerRow[];
3877
+ byStatus: ByStatusRow[];
3878
+ byCategory: ByCategoryRow[];
3879
+ timeline: TimelinePoint[];
3880
+ approvals: ReportApprovals;
3881
+ comparison: Comparison;
3882
+ observations: Observation[];
3883
+ dataQuality: DataQuality;
3884
+ }
3885
+ export interface ByCounterpartyAddressRow {
3886
+ /** Запись справочника адресов контрагента; null — адрес в справочник не внесён */
3887
+ counterpartyAddressId: string | null;
3888
+ /** Внешний адрес (on-chain) */
3889
+ address: string;
3890
+ /** Network.slug */
3891
+ network: string;
3892
+ /** Задел; сейчас всегда null */
3893
+ label: string | null;
3894
+ incomingByAsset: AssetAmount[];
3895
+ outgoingByAsset: AssetAmount[];
3896
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3897
+ grossTurnoverUsd: string | null;
3898
+ operationsCount: number;
3899
+ incomingCount: number;
3900
+ outgoingCount: number;
3901
+ /** Доля адреса в USD-обороте отчёта, проценты decimal-строкой; null — нет USD-оценки или оборот нулевой */
3902
+ turnoverShareUsd: string | null;
3903
+ firstOperationAt: string | null;
3904
+ lastOperationAt: string | null;
3905
+ /** true — до начала периода операций с этим адресом не было (в любом статусе, включая внутренние потоки) */
3906
+ isNewInPeriod: boolean;
3907
+ }
3908
+ export interface CounterpartyGroupActivityReport {
3909
+ report: ReportMeta;
3910
+ group: {
3911
+ id: string;
3912
+ code: string;
3913
+ name: string;
3914
+ /** Сколько контрагентов в группе СЕЙЧАС. При counterpartyClassification=historical выборка идёт по составу на момент отчёта и может не совпасть с этим числом */
3915
+ counterpartiesCount: number;
3916
+ };
3917
+ summary: ReportSummary;
3918
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
3919
+ byAddressOwner?: ByAddressOwnerRow[];
3920
+ byCounterparty: ByGroupCounterpartyRow[];
3921
+ byAsset: ByAssetRow[];
3922
+ byStatus: ByStatusRow[];
3923
+ byCategory: ByCategoryRow[];
3924
+ timeline: TimelinePoint[];
3925
+ approvals: ReportApprovals;
3926
+ comparison: Comparison;
3927
+ observations: Observation[];
3928
+ dataQuality: DataQuality;
3929
+ }
3930
+ export interface ByGroupCounterpartyRow {
3931
+ counterpartyId: string | null;
3932
+ name: string | null;
3933
+ kind: string | null;
3934
+ incomingByAsset: AssetAmount[];
3935
+ outgoingByAsset: AssetAmount[];
3936
+ /** Приток минус отток по активу, со знаком */
3937
+ netFlowByAsset: AssetAmount[];
3938
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3939
+ grossTurnoverUsd: string | null;
3940
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
3941
+ netFlowUsd: string | null;
3942
+ operationsCount: number;
3943
+ /** Доля контрагента в USD-обороте группы, проценты decimal-строкой ("37.5"); null — у оборота нет USD-оценки или он нулевой */
3944
+ shareOfGroupTurnoverUsd: string | null;
3945
+ firstOperationAt: string | null;
3946
+ lastOperationAt: string | null;
3663
3947
  }
3664
- export interface Comparison {
3665
- mode: string;
3666
- previousFrom: string | null;
3667
- previousTo: string | null;
3668
- incomingUsdChangePercent: number | null;
3669
- outgoingUsdChangePercent: number | null;
3670
- grossTurnoverUsdChangePercent: number | null;
3671
- operationsCountChangePercent: number | null;
3672
- averageOperationUsdChangePercent: number | null;
3948
+ export interface AssetMovementReport {
3949
+ report: ReportMeta;
3950
+ asset: {
3951
+ tokenId: string;
3952
+ /** Token.code из реестра */
3953
+ code: string;
3954
+ symbol: string;
3955
+ name: string;
3956
+ decimals: number;
3957
+ /** Network.slug */
3958
+ network: string;
3959
+ /** null у нативного актива сети */
3960
+ contractAddress: string | null;
3961
+ };
3962
+ /** Остаток актива на начало периода (from), из ledger. DECIMAL в единицах актива — не raw, в отличие от openingBalances у address_activity */
3963
+ openingBalance: {
3964
+ /** decimal */
3965
+ available: string;
3966
+ /** decimal */
3967
+ reserved: string;
3968
+ /** available + reserved, decimal */
3969
+ total: string;
3970
+ };
3971
+ /** Остаток актива на конец периода (to), из ledger. DECIMAL в единицах актива — не raw, в отличие от openingBalances у address_activity */
3972
+ closingBalance: {
3973
+ /** decimal */
3974
+ available: string;
3975
+ /** decimal */
3976
+ reserved: string;
3977
+ /** available + reserved, decimal */
3978
+ total: string;
3979
+ };
3980
+ summary: ReportSummary;
3981
+ byCounterparty: ByCounterpartyBriefRow[];
3982
+ byOwnAddress: ByOwnAddressRow[];
3983
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
3984
+ byAddressOwner?: ByAddressOwnerRow[];
3985
+ byStatus: ByStatusRow[];
3986
+ byCategory: ByCategoryRow[];
3987
+ timeline: TimelinePoint[];
3988
+ comparison: Comparison;
3989
+ observations: Observation[];
3990
+ dataQuality: DataQuality;
3991
+ }
3992
+ export interface ByCounterpartyBriefRow {
3993
+ /** null — операции без сопоставленного контрагента (одна строка на все) */
3994
+ counterpartyId: string | null;
3995
+ /** Имя контрагента. У строки без контрагента (counterpartyId = null) сюда приходит английская заглушка "unmatched counterparty" — она не локализуется, поэтому различать такую строку надо по counterpartyId */
3996
+ name: string | null;
3997
+ incomingByAsset: AssetAmount[];
3998
+ outgoingByAsset: AssetAmount[];
3999
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4000
+ grossTurnoverUsd: string | null;
4001
+ operationsCount: number;
3673
4002
  }
3674
- export interface BalancesAsOf {
3675
- /** Момент среза (echo параметра ?at=) */
4003
+ export interface AddressActivityReport {
4004
+ report: ReportMeta;
4005
+ /** Цель отчёта, и форма у неё ДВЕ: при addressId — карточка адреса, при folderId — {folder:{id,name}}. Имя ключа одно на оба случая исторически */
4006
+ address: {
4007
+ id: string;
4008
+ address: string;
4009
+ label: string | null;
4010
+ description: string | null;
4011
+ /** Без апострофов: m/44/195/… */
4012
+ derivationPath: string;
4013
+ segmentsJson: {
4014
+ [key: string]: unknown;
4015
+ };
4016
+ /** Network.slug */
4017
+ network: string;
4018
+ createdAt: string;
4019
+ } | {
4020
+ folder: {
4021
+ id: string;
4022
+ name: string;
4023
+ };
4024
+ };
4025
+ /** Папки адреса. Вторая дата зависит от folderClassification: historical (по умолчанию) — validFrom из истории членства, current — createdAt текущего членства. При folderId — всегда пустой массив */
4026
+ folderMemberships: Array<{
4027
+ folderId: string;
4028
+ validFrom: string;
4029
+ } | {
4030
+ folderId: string;
4031
+ createdAt: string;
4032
+ }>;
4033
+ /** Балансы счетов на начало периода (from), RAW. При addressId — счета этого адреса. При folderId выборка НЕ сужается до папки: приходят все счета scope вызывающего (у admin — весь Vault, включая external и adjustment) */
4034
+ openingBalances: ReportAccountBalance[];
4035
+ /** Балансы счетов на конец периода (to), RAW. При addressId — счета этого адреса. При folderId выборка НЕ сужается до папки: приходят все счета scope вызывающего (у admin — весь Vault, включая external и adjustment) */
4036
+ closingBalances: ReportAccountBalance[];
4037
+ summary: ReportSummary;
4038
+ /** Только при ?breakdownBy=owner. Без параметра ключа в ответе НЕТ — это не то же самое, что пустой массив */
4039
+ byAddressOwner?: ByAddressOwnerRow[];
4040
+ /** Доля адреса (папки) в USD-обороте всего scope за тот же период, проценты decimal-строкой; null — нет USD-оценки или оборот scope нулевой */
4041
+ shareOfUserActivityUsd: string | null;
4042
+ byAsset: ByAssetRow[];
4043
+ byCounterparty: ByCounterpartyBriefRow[];
4044
+ byStatus: ByStatusRow[];
4045
+ byCategory: ByCategoryRow[];
4046
+ timeline: TimelinePoint[];
4047
+ observations: Observation[];
4048
+ dataQuality: DataQuality;
4049
+ }
4050
+ export interface ReportAccountBalance {
4051
+ accountId: string;
4052
+ /** available / reserved — счета адресов; external / adjustment — счета уровня Vault'а (без адреса): встречная сторона двойной записи */
4053
+ purpose: "available" | "reserved" | "external" | "adjustment";
4054
+ tokenId: string;
4055
+ /** null у счетов уровня Vault'а */
4056
+ addressId: string | null;
4057
+ /** Raw smallest units со знаком — НЕ decimal */
4058
+ balanceRaw: string;
4059
+ }
4060
+ export interface OperationsExceptionsReport {
4061
+ report: ReportMeta;
4062
+ summary: {
4063
+ operationsCount: number;
4064
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4065
+ usdValue: string | null;
4066
+ /** null — выборка пуста */
4067
+ oldestOperationAt: string | null;
4068
+ /** Возраст считается от createdAt до момента построения отчёта */
4069
+ averageAgeSeconds: number | null;
4070
+ medianAgeSeconds: number | null;
4071
+ };
4072
+ /** Всегда шесть строк в этом порядке, включая пустые корзины */
4073
+ ageBuckets: Array<{
4074
+ bucket: "0-15m" | "15-60m" | "1-6h" | "6-24h" | "1-3d" | ">3d";
4075
+ count: number;
4076
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4077
+ usdValue: string | null;
4078
+ }>;
4079
+ byStatus: ByStatusRow[];
4080
+ byAsset: ByAssetRow[];
4081
+ byCounterparty: Array<{
4082
+ /** null — операции без сопоставленного контрагента (одна строка на все) */
4083
+ counterpartyId: string | null;
4084
+ /** Имя контрагента. У строки без контрагента (counterpartyId = null) сюда приходит английская заглушка "unmatched counterparty" — она не локализуется, поэтому различать такую строку надо по counterpartyId */
4085
+ name: string | null;
4086
+ count: number;
4087
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4088
+ usdValue: string | null;
4089
+ }>;
4090
+ byInitiator: Array<{
4091
+ /** null — у операции нет инициатора (депозиты) */
4092
+ initiatorId: string | null;
4093
+ count: number;
4094
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4095
+ usdValue: string | null;
4096
+ }>;
4097
+ byDecisionPolicy: Array<{
4098
+ /** null — решение по операции политика не принимала */
4099
+ decisionPolicyId: string | null;
4100
+ code: string | null;
4101
+ count: number;
4102
+ }>;
4103
+ dataQuality: DataQuality;
4104
+ }
4105
+ export interface ApprovalActivityReport {
4106
+ report: ReportMeta;
4107
+ summary: {
4108
+ requestsCount: number;
4109
+ pendingCount: number;
4110
+ approvedCount: number;
4111
+ rejectedCount: number;
4112
+ cancelledCount: number;
4113
+ /** approved / (approved + rejected) × 100, decimal-строка; null — решённых заявок нет. Отменённые в знаменатель не входят */
4114
+ approvalRatePercent: string | null;
4115
+ rejectionRatePercent: string | null;
4116
+ /** cancelled / все заявки × 100; null — заявок нет */
4117
+ cancellationRatePercent: string | null;
4118
+ /** От создания заявки до resolvedAt; null — решённых нет */
4119
+ averageResolutionSeconds: number | null;
4120
+ medianResolutionSeconds: number | null;
4121
+ maximumResolutionSeconds: number | null;
4122
+ /** От создания заявки до первого голоса; null — голосов нет */
4123
+ averageFirstVoteSeconds: number | null;
4124
+ medianFirstVoteSeconds: number | null;
4125
+ requestsWithoutVotes: number;
4126
+ };
4127
+ byAction: Array<{
4128
+ /** ApprovalRequest.action: transfer, composite… */
4129
+ action: string;
4130
+ count: number;
4131
+ approvedCount: number;
4132
+ rejectedCount: number;
4133
+ cancelledCount: number;
4134
+ pendingCount: number;
4135
+ averageResolutionSeconds: number | null;
4136
+ }>;
4137
+ byInitiator: Array<{
4138
+ userId: string;
4139
+ name: string;
4140
+ requestsCount: number;
4141
+ approvedCount: number;
4142
+ rejectedCount: number;
4143
+ cancelledCount: number;
4144
+ averageResolutionSeconds: number | null;
4145
+ }>;
4146
+ byApprover: Array<{
4147
+ userId: string;
4148
+ /** Имя; если пользователь не найден — его userId */
4149
+ name: string;
4150
+ assignedRequestsCount: number;
4151
+ votedCount: number;
4152
+ approveVotes: number;
4153
+ rejectVotes: number;
4154
+ pendingVotes: number;
4155
+ averageVoteSeconds: number | null;
4156
+ medianVoteSeconds: number | null;
4157
+ }>;
4158
+ byDecisionPolicy: Array<{
4159
+ decisionPolicyId: string | null;
4160
+ code: string | null;
4161
+ /** Задел; сейчас всегда null */
4162
+ name: string | null;
4163
+ triggeredCount: number;
4164
+ approvedCount: number;
4165
+ rejectedCount: number;
4166
+ pendingCount: number;
4167
+ averageResolutionSeconds: number | null;
4168
+ }>;
4169
+ timeline: Array<{
4170
+ periodStart: string;
4171
+ createdCount: number;
4172
+ /** approved + rejected; отменённые не входят */
4173
+ resolvedCount: number;
4174
+ approvedCount: number;
4175
+ rejectedCount: number;
4176
+ cancelledCount: number;
4177
+ }>;
4178
+ /** Только эхо параметра comparison: процентов изменения этот отчёт НЕ считает. У живого отчёта previous_year приходит как none; в снапшоте и при запуске шаблона значение не переводится и приходит как прислано */
4179
+ comparison: {
4180
+ mode: "none" | "previous_period" | "previous_year";
4181
+ };
4182
+ dataQuality: DataQuality;
4183
+ }
4184
+ export interface BalancePositionReport {
4185
+ report: ReportMeta;
4186
+ /** Момент позиции, ISO-8601 UTC */
3676
4187
  asOf: string;
3677
- /** @defaultValue [] */
3678
- totals: Array<{
3679
- [key: string]: unknown;
4188
+ totalsByAsset: Array<{
4189
+ tokenId: string;
4190
+ assetCode: string;
4191
+ assetSymbol: string;
4192
+ /** Network.slug */
4193
+ network: string;
4194
+ /** decimal */
4195
+ available: string;
4196
+ /** decimal */
4197
+ reserved: string;
4198
+ /** available + reserved, decimal */
4199
+ total: string;
4200
+ usdRate: string | null;
4201
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4202
+ usdValue: string | null;
3680
4203
  }>;
3681
- /** @defaultValue [] */
3682
- accounts: Array<{
3683
- [key: string]: unknown;
4204
+ byAddress: Array<{
4205
+ addressId: string;
4206
+ address: string;
4207
+ label: string | null;
4208
+ balances: Array<{
4209
+ tokenId: string;
4210
+ /** decimal */
4211
+ available: string;
4212
+ /** decimal */
4213
+ reserved: string;
4214
+ /** decimal */
4215
+ total: string;
4216
+ /** USD-оценка, decimal-строка. null — курса на момент операции не было */
4217
+ usdValue: string | null;
4218
+ }>;
3684
4219
  }>;
4220
+ concentration: {
4221
+ /** Доля крупнейшего адреса в USD, проценты decimal-строкой; null без USD-оценки */
4222
+ largestAddressShareUsd: string | null;
4223
+ /** Доля пяти крупнейших адресов, проценты decimal-строкой; null без USD-оценки */
4224
+ top5AddressesShareUsd: string | null;
4225
+ };
4226
+ dataQuality: DataQuality;
3685
4227
  }
4228
+ /** Один из десяти отчётов. Форму определяет report.type: у каждого типа своя схема, общих для всех блоков — только report и (кроме category_analytics) dataQuality */
4229
+ export type AnyReport = TransactionActivityReport | CounterpartyActivityReport | CounterpartyGroupActivityReport | AssetMovementReport | AddressActivityReport | AccountStatementReport | OperationsExceptionsReport | ApprovalActivityReport | BalancePositionReport | CategoryAnalytics;
3686
4230
  export interface CategoryAnalytics {
3687
- report: ReportMeta;
4231
+ report: {
4232
+ /** Тип отчёта, snake_case — тот же, что в пути и в ?type= */
4233
+ type: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics";
4234
+ generatedAt: string;
4235
+ /** Начало периода (включительно), ISO-8601 UTC. У balance_position from = to = момент позиции */
4236
+ from: string;
4237
+ /** Конец периода (НЕ включительно), ISO-8601 UTC */
4238
+ to: string;
4239
+ /** IANA-зона, в которой нарезан timeline. У balance_position всегда UTC */
4240
+ timezone: string;
4241
+ };
3688
4242
  categoryKey: string;
4243
+ /** Операции с активным назначением этой категории */
3689
4244
  operationsCount: number;
4245
+ /** null — metricField не задан */
3690
4246
  metric: {
3691
4247
  fieldKey: string;
3692
- agg: string;
4248
+ agg: "sum" | "avg" | "min" | "max" | "median" | "count";
3693
4249
  /** null, когда метрика деноминирована — см. byCurrency */
3694
4250
  value: string | null;
3695
4251
  byCurrency: {
3696
4252
  [key: string]: unknown;
3697
4253
  } | null;
3698
4254
  } | null;
4255
+ /** null — groupByField не задан */
3699
4256
  groups: Array<{
4257
+ /** Значение поля groupByField; null — у назначения поле не заполнено */
3700
4258
  key: string | null;
4259
+ /** Число НАЗНАЧЕНИЙ категории в группе, а не операций: у одной операции их может быть несколько, поэтому сумма по группам бывает больше operationsCount отчёта */
3701
4260
  operationsCount: number;
3702
4261
  metric: string | null;
3703
4262
  metricByCurrency: {
@@ -3705,6 +4264,74 @@ export interface CategoryAnalytics {
3705
4264
  } | null;
3706
4265
  }> | null;
3707
4266
  }
4267
+ export interface BalancesAsOfAccount {
4268
+ accountId: string;
4269
+ /** available / reserved — счета адресов; external / adjustment — счета уровня Vault'а (без адреса): встречная сторона двойной записи */
4270
+ purpose: "available" | "reserved" | "external" | "adjustment";
4271
+ tokenId: string;
4272
+ /** null у vault-level счетов */
4273
+ addressId: string | null;
4274
+ /** Raw smallest units со знаком (vault-level счета бывают отрицательными) */
4275
+ balance: string;
4276
+ /** То же в единицах актива. Если токена счёта нет в реестре — raw, как в balance */
4277
+ balanceDecimal: string;
4278
+ }
4279
+ export interface BalancesAsOf {
4280
+ /** Момент среза: параметр ?at=, приведённый к ISO-8601 в UTC (не буквальное эхо) */
4281
+ asOf: string;
4282
+ /** По строке на токен; складываются только available и reserved. Пустой массив — движений до момента среза не было (или ?purpose= выбрал external/adjustment): это «нет данных», а не «ноль» */
4283
+ totals: BalancesAsOfTotal[];
4284
+ /** Счета, по которым были движения до момента среза. ОТСУТСТВУЕТ при ?summary=true. Участник видит только счета адресов из своего scope; vault-level счета (external/adjustment) — только admin */
4285
+ accounts?: BalancesAsOfAccount[];
4286
+ }
4287
+ export interface BalancesAsOfTotal {
4288
+ token: {
4289
+ code: string;
4290
+ symbol: string;
4291
+ decimals: number;
4292
+ /** Network.slug */
4293
+ network: string;
4294
+ };
4295
+ tokenId: string;
4296
+ /** DECIMAL (не raw!) — дубль availableDecimal; у GET /balances это поле raw */
4297
+ available: string;
4298
+ availableDecimal: string;
4299
+ /** DECIMAL (не raw!) — дубль reservedDecimal; у GET /balances это поле raw */
4300
+ reserved: string;
4301
+ reservedDecimal: string;
4302
+ /** available + reserved, decimal: резерв — ещё не расход, пока перевод не подтверждён */
4303
+ ownedTotal: string;
4304
+ }
4305
+ export interface CreateReportExportBody {
4306
+ /** json — canonical JSON результата; csv — плоский реестр операций. xlsx и pdf схема принимает, а хендлер отклоняет: 400 REPORT_UNSUPPORTED_FORMAT */
4307
+ format: "json" | "csv" | "xlsx" | "pdf";
4308
+ /** Зарезервировано: сейчас не влияет на файл */
4309
+ locale?: string;
4310
+ /** Зарезервировано: сейчас не влияет на файл */
4311
+ timezone?: string;
4312
+ /** Зарезервировано: сейчас не влияет на файл (csv — всегда реестр, json — всегда результат) */
4313
+ includeOperationRegister?: boolean;
4314
+ }
4315
+ export interface CreateReportSnapshotBody {
4316
+ reportType: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics";
4317
+ /** Те же параметры, что уходили в живой отчёт query-строкой (from, to, timezone, фильтры, includeShared, цель отчёта). Сохраняются в снапшоте как есть — под именем `parametersJson` */
4318
+ parameters: {
4319
+ [key: string]: unknown;
4320
+ };
4321
+ }
4322
+ export interface CreateReportTemplateBody {
4323
+ /** length: 1..120 */
4324
+ name: string;
4325
+ /** length: 0..500 */
4326
+ description?: string;
4327
+ reportType: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics";
4328
+ /** Параметры отчёта, как в query живого отчёта. Вместо from/to можно задать relativePeriod {unit: day|week|month, offset} — даты вычислятся при запуске. В ответе лежат под именем `parametersJson` */
4329
+ parameters: {
4330
+ [key: string]: unknown;
4331
+ };
4332
+ /** true — только admin */
4333
+ isShared?: boolean;
4334
+ }
3708
4335
  export interface DrilldownResponse {
3709
4336
  /** Операции с полями и categoryAssignments (sensitive-значения отредактированы по правам: значение null + ключ в redactedFields) */
3710
4337
  items: Array<{
@@ -3719,92 +4346,241 @@ export interface DrilldownResponse {
3719
4346
  usdValue: string | null;
3720
4347
  };
3721
4348
  }
4349
+ export interface LedgerEntriesPage {
4350
+ items: LedgerEntry[];
4351
+ nextCursor: string | null;
4352
+ }
3722
4353
  export interface LedgerEntry {
3723
4354
  id: string;
3724
4355
  vaultId: string;
3725
- /** deposit | transfer | adjustment … */
4356
+ /** topup | transfer_reserve | transfer_confirm | transfer_refund | adjustment | genesis. Это НЕ хранимый kind: три вида переименованы на выходе (reserve → transfer_reserve, confirm → transfer_confirm, release → transfer_refund), остальные отдаются как есть. Фильтр ?entryType= понимает оба написания */
3726
4357
  entryType: string;
3727
- reference: string | null;
3728
- description: string | null;
3729
- operationType: string | null;
4358
+ /** Документ-источник; null у корректировок и genesis */
4359
+ operationType: "transfer" | "deposit" | null;
4360
+ /** id перевода или депозита — по operationType */
3730
4361
  operationId: string | null;
3731
- token: {
3732
- [key: string]: unknown;
3733
- } | null;
3734
- effectiveAt: string;
4362
+ /** Время записи в журнал. По нему идут сортировка, фильтр from/to и балансы as-of */
3735
4363
  postedAt: string;
4364
+ /** Экономическое время события (для on-chain — время блока) */
4365
+ effectiveAt: string;
4366
+ /** Ключ идемпотентности проводки (sourceKey), уникален: "topup:{eventId}", "reserve:{transferId}", "confirm:{transferId}", "release:{transferId}" */
4367
+ reference: string;
4368
+ /** Примечание к проводке (memo) */
4369
+ description: string | null;
4370
+ /** Токен проводки — по её ПЕРВОМУ постингу, взятому до фильтрации под права: присутствует, даже если postings у вызывающего пуст */
4371
+ token: LedgerEntryToken | null;
4372
+ postings: LedgerPosting[];
4373
+ /** То же значение, что postedAt */
3736
4374
  createdAt: string;
3737
- postings: Array<{
3738
- id: string;
3739
- accountId: string;
3740
- amount: string;
3741
- amountDecimal?: string;
3742
- direction?: string;
3743
- token?: {
3744
- [key: string]: unknown;
3745
- } | null;
3746
- account?: {
3747
- [key: string]: unknown;
3748
- } | null;
3749
- }>;
4375
+ }
4376
+ export interface LedgerEntryToken {
4377
+ id: string;
4378
+ code: string;
4379
+ symbol: string;
4380
+ decimals: number;
4381
+ /** Network.slug */
4382
+ network: string;
4383
+ /** null у нативного актива сети */
4384
+ contractAddress: string | null;
4385
+ }
4386
+ export interface LedgerPosting {
4387
+ id: string;
4388
+ accountId: string;
4389
+ /** available / reserved — счета адресов; external / adjustment — счета уровня Vault'а (без адреса): встречная сторона двойной записи */
4390
+ accountPurpose: "available" | "reserved" | "external" | "adjustment";
4391
+ /** address — счёт адреса; vault — счёт уровня Vault'а (external/adjustment) */
4392
+ ownerType: "address" | "vault";
4393
+ /** addressId для ownerType=address, иначе vaultId */
4394
+ ownerId: string;
4395
+ /** null у vault-level счетов */
4396
+ addressId: string | null;
4397
+ /** Raw smallest units со знаком: "2500000" | "-2500000" */
4398
+ amountRawSigned: string;
4399
+ /** Та же сумма в единицах актива, со знаком: "-2.5". Если токена постинга нет в реестре (чего при живом FK быть не должно), сюда попадает raw — значение совпадёт с amountRawSigned */
4400
+ amountDecimalSigned: string;
4401
+ }
4402
+ export interface PatchReportTemplateBody {
4403
+ /** length: 1..120 */
4404
+ name?: string;
4405
+ /** length: 0..500 */
4406
+ description?: string;
4407
+ reportType?: "counterparty_activity" | "counterparty_group_activity" | "transaction_activity" | "asset_movement" | "address_activity" | "balance_position" | "account_statement" | "approval_activity" | "operations_exceptions" | "category_analytics";
4408
+ /** Параметры отчёта, как в query живого отчёта. Вместо from/to можно задать relativePeriod {unit: day|week|month, offset} — даты вычислятся при запуске. В ответе лежат под именем `parametersJson` */
4409
+ parameters?: {
4410
+ [key: string]: unknown;
4411
+ };
4412
+ /** true — только admin */
4413
+ isShared?: boolean;
4414
+ }
4415
+ export interface ReportDrilldownBody {
4416
+ reportType?: string;
4417
+ /** @defaultValue {} */
4418
+ filters?: {
4419
+ [key: string]: unknown;
4420
+ };
4421
+ /** @defaultValue {} */
4422
+ drilldown?: {
4423
+ [key: string]: unknown;
4424
+ };
4425
+ snapshotId?: string | null;
4426
+ /**
4427
+ * range: 1..500
4428
+ * @defaultValue 100
4429
+ */
4430
+ limit?: number;
4431
+ cursor?: string | null;
3750
4432
  }
3751
4433
  export interface ReportExport {
3752
4434
  id: string;
4435
+ vaultId: string;
3753
4436
  snapshotId: string;
4437
+ /** json | csv */
3754
4438
  format: string;
4439
+ /** queued | processing | completed | failed; экспорт синхронный, поэтому сейчас всегда completed */
3755
4440
  status: string;
3756
- filename: string | null;
4441
+ requestedById: string;
4442
+ requestedAt: string;
4443
+ completedAt: string | null;
4444
+ /** Задел под R2; сейчас всегда null */
4445
+ storageKey: string | null;
3757
4446
  mimeType: string | null;
4447
+ filename: string | null;
4448
+ /** Длина содержимого в СИМВОЛАХ, а не в байтах: для не-ASCII текста (кириллица в подписях адресов) файл на диске будет больше */
3758
4449
  sizeBytes: number | null;
4450
+ /** sha256 содержимого файла */
3759
4451
  fileHash: string | null;
3760
- requestedById: string;
3761
- completedAt: string | null;
4452
+ errorCode: string | null;
4453
+ errorMessage: string | null;
3762
4454
  }
3763
- export interface ReportSnapshot {
4455
+ export interface ReportSnapshotDetail {
3764
4456
  id: string;
3765
- vaultId: string;
3766
4457
  /** snake_case, как и везде */
3767
4458
  reportType: string;
3768
- status: string;
3769
- schemaVersion: string | null;
4459
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
4460
+ schemaVersion: string;
4461
+ /** Тело `parameters` запроса создания, как есть */
3770
4462
  parametersJson: {
3771
4463
  [key: string]: unknown;
3772
- } | null;
3773
- /** Хеш нормализованных параметров — одинаковый запрос даёт одинаковый хеш */
3774
- normalizedParametersHash: string | null;
3775
- result?: {
3776
- [key: string]: unknown;
3777
- } | null;
3778
- /** canonical sha256 результата — доказательство, что содержимое не подменили */
3779
- resultHash: string | null;
3780
- /** Задел под R2; сейчас всегда null */
3781
- resultStorageKey: string | null;
3782
- rowCount: number | null;
3783
- /** Манифест: ключи операций, вошедших в снапшот (для drill-down по снапшоту) */
3784
- includedOperationKeysJson?: string[] | null;
3785
- includedOperationKeysStorageKey?: string | null;
3786
- dataCutoffAt: string | null;
4464
+ };
4465
+ generatedById: string;
4466
+ generatedAt: string;
4467
+ /** Срез данных: операции новее в снапшот не вошли. Совпадает с generatedAt */
4468
+ dataCutoffAt: string;
4469
+ /** Число операций в манифесте; 0, если манифеста нет — у отчётов без пооперационной выборки (balance_position, approval_activity) */
4470
+ rowCount: number;
4471
+ /** sha256 canonical JSON результата — доказательство, что содержимое не подменили */
4472
+ resultHash: string;
4473
+ /** generating | completed | failed; создание синхронное, поэтому сейчас всегда completed */
4474
+ status: string;
4475
+ createdAt: string;
4476
+ vaultId: string;
4477
+ /** sha256 canonical JSON параметров — одинаковый запрос даёт одинаковый хеш */
4478
+ normalizedParametersHash: string;
4479
+ /** Задел; сейчас всегда null */
3787
4480
  dataCutoffBlock: number | null;
4481
+ /** current | historical — из параметра counterpartyClassification, по умолчанию historical */
3788
4482
  counterpartyClassificationMode: string | null;
4483
+ /** current | historical — из параметра folderClassification, по умолчанию historical */
3789
4484
  folderClassificationMode: string | null;
4485
+ /** Задел под R2; сейчас всегда null */
4486
+ resultStorageKey: string | null;
4487
+ /** Манифест: ключи операций, вошедших в снапшот (по нему строится drill-down по снапшоту). null — у отчёта нет пооперационной выборки или манифест собрать не удалось */
4488
+ includedOperationKeysJson: string[] | null;
4489
+ /** Задел под R2; сейчас всегда null */
4490
+ includedOperationKeysStorageKey: string | null;
3790
4491
  errorCode: string | null;
3791
4492
  errorMessage: string | null;
4493
+ /** В ответах всегда null: скрытый (soft-delete) снапшот не отдаётся вовсе — 404 */
4494
+ deletedAt: string | null;
4495
+ /** Результат отчёта, зафиксированный при создании. В ответе создания то же самое называется `result`. null возможен только у снапшота, который не достроился (status ≠ completed) */
4496
+ resultJson: {
4497
+ [key: string]: unknown;
4498
+ } | null;
4499
+ }
4500
+ export interface ReportSnapshotListItem {
4501
+ id: string;
4502
+ /** snake_case, как и везде */
4503
+ reportType: string;
4504
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
4505
+ schemaVersion: string;
4506
+ /** Тело `parameters` запроса создания, как есть */
4507
+ parametersJson: {
4508
+ [key: string]: unknown;
4509
+ };
4510
+ generatedById: string;
4511
+ generatedAt: string;
4512
+ /** Срез данных: операции новее в снапшот не вошли. Совпадает с generatedAt */
4513
+ dataCutoffAt: string;
4514
+ /** Число операций в манифесте; 0, если манифеста нет — у отчётов без пооперационной выборки (balance_position, approval_activity) */
4515
+ rowCount: number;
4516
+ /** sha256 canonical JSON результата — доказательство, что содержимое не подменили */
4517
+ resultHash: string;
4518
+ /** generating | completed | failed; создание синхронное, поэтому сейчас всегда completed */
4519
+ status: string;
4520
+ createdAt: string;
4521
+ }
4522
+ export interface ReportSnapshotList {
4523
+ items: ReportSnapshotListItem[];
4524
+ }
4525
+ export interface ReportSnapshot {
4526
+ id: string;
4527
+ /** snake_case, как и везде */
4528
+ reportType: string;
4529
+ /** Версия формата результата; сейчас всегда "reporting_v1" */
4530
+ schemaVersion: string;
4531
+ /** Тело `parameters` запроса создания, как есть */
4532
+ parametersJson: {
4533
+ [key: string]: unknown;
4534
+ };
3792
4535
  generatedById: string;
3793
4536
  generatedAt: string;
4537
+ /** Срез данных: операции новее в снапшот не вошли. Совпадает с generatedAt */
4538
+ dataCutoffAt: string;
4539
+ /** Число операций в манифесте; 0, если манифеста нет — у отчётов без пооперационной выборки (balance_position, approval_activity) */
4540
+ rowCount: number;
4541
+ /** sha256 canonical JSON результата — доказательство, что содержимое не подменили */
4542
+ resultHash: string;
4543
+ /** generating | completed | failed; создание синхронное, поэтому сейчас всегда completed */
4544
+ status: string;
3794
4545
  createdAt: string;
4546
+ vaultId: string;
4547
+ /** sha256 canonical JSON параметров — одинаковый запрос даёт одинаковый хеш */
4548
+ normalizedParametersHash: string;
4549
+ /** Задел; сейчас всегда null */
4550
+ dataCutoffBlock: number | null;
4551
+ /** current | historical — из параметра counterpartyClassification, по умолчанию historical */
4552
+ counterpartyClassificationMode: string | null;
4553
+ /** current | historical — из параметра folderClassification, по умолчанию historical */
4554
+ folderClassificationMode: string | null;
4555
+ /** Задел под R2; сейчас всегда null */
4556
+ resultStorageKey: string | null;
4557
+ /** Манифест: ключи операций, вошедших в снапшот (по нему строится drill-down по снапшоту). null — у отчёта нет пооперационной выборки или манифест собрать не удалось */
4558
+ includedOperationKeysJson: string[] | null;
4559
+ /** Задел под R2; сейчас всегда null */
4560
+ includedOperationKeysStorageKey: string | null;
4561
+ errorCode: string | null;
4562
+ errorMessage: string | null;
4563
+ /** В ответах всегда null: скрытый (soft-delete) снапшот не отдаётся вовсе — 404 */
3795
4564
  deletedAt: string | null;
4565
+ /** Результат отчёта — тот же объект, что вернул бы живой отчёт. ТОЛЬКО в ответе создания; деталь отдаёт его как `resultJson` */
4566
+ result: {
4567
+ [key: string]: unknown;
4568
+ };
3796
4569
  }
3797
4570
  export interface ReportTemplate {
3798
4571
  id: string;
3799
4572
  vaultId: string;
4573
+ /** Создатель шаблона */
4574
+ ownerId: string;
3800
4575
  name: string;
4576
+ description: string | null;
3801
4577
  reportType: string;
4578
+ /** Тело `parameters` запроса создания, как есть (включая relativePeriod) */
3802
4579
  parametersJson: {
3803
4580
  [key: string]: unknown;
3804
- } | null;
4581
+ };
3805
4582
  isShared: boolean;
3806
- createdById: string;
3807
4583
  createdAt: string;
3808
- updatedAt?: string;
4584
+ updatedAt: string;
3809
4585
  }
3810
4586
  //# sourceMappingURL=types.d.ts.map