@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
@@ -10,9 +10,33 @@ import type { HistoryItem } from '../generated/types.js'
10
10
  * client from two cursors.
11
11
  */
12
12
 
13
+ /**
14
+ * The statuses the feed can be FILTERED by.
15
+ *
16
+ * One value short of `TransactionStatus`, which is the right set for reading
17
+ * a transfer: `submitted` is missing here. It is a legacy status — still the
18
+ * column default in the schema, so old rows carry it — and the endpoint's
19
+ * enum does not accept it, so filtering by it is a 400 rather than an empty
20
+ * page. Hence a separate type: taking the reader's union would have the
21
+ * package suggest a value its own filter rejects.
22
+ */
23
+ export type HistoryStatusFilter =
24
+ | 'awaiting_approval'
25
+ | 'pending'
26
+ | 'confirmed'
27
+ | 'failed'
28
+ | 'rejected'
29
+ | 'cancelled'
30
+
13
31
  export interface HistoryParams extends PageParams {
14
32
  direction?: 'in' | 'out' | undefined
15
- status?: string | undefined
33
+ /**
34
+ * Outgoing only, whichever value you pass: a status belongs to a transfer,
35
+ * so setting this EXCLUDES the incoming side of the feed even without
36
+ * `direction: 'out'`. The same goes for `awaitingMyApproval` and
37
+ * `decisionPolicyId`.
38
+ */
39
+ status?: HistoryStatusFilter | undefined
16
40
  /** Only what awaits MY signature. For the approvals screen. */
17
41
  awaitingMyApproval?: boolean | undefined
18
42
  asset?: string | undefined
@@ -23,7 +47,8 @@ export interface HistoryParams extends PageParams {
23
47
  counterpartyId?: string | undefined
24
48
  counterpartyGroupId?: string | undefined
25
49
  counterpartyAddressId?: string | undefined
26
- flowType?: string | undefined
50
+ /** `internal` — the other side is an address of this vault; `external` — it is not. */
51
+ flowType?: 'internal' | 'external' | undefined
27
52
  category?: string | undefined
28
53
  paymentPurpose?: string | undefined
29
54
  decisionPolicyId?: string | undefined
@@ -1,6 +1,7 @@
1
1
  import type { HttpClient } from '../core/http.js'
2
2
  import type {
3
3
  AllowedSegmentsResponse,
4
+ MeVaultResponse,
4
5
  PatchProfileBody,
5
6
  PushToken,
6
7
  RegisterPushTokenBody,
@@ -19,6 +20,14 @@ export class MeResource {
19
20
  return this.#http.request<UserProfile>('GET', '/api/v1/me/profile')
20
21
  }
21
22
 
23
+ /**
24
+ * Partial update of your own profile: `name`, `username`, `image`, `color`,
25
+ * `emoji`. `null` clears a field (except `name`). `username` is unique
26
+ * across the whole platform — taken answers 409 `USERNAME_TAKEN`.
27
+ *
28
+ * Answers with the full profile, the same shape `profile()` returns —
29
+ * `needsEmail` included, so there is no need to re-read it after an update.
30
+ */
22
31
  updateProfile(input: PatchProfileBody): Promise<UserProfile> {
23
32
  return this.#http.request<UserProfile>('PATCH', '/api/v1/me/profile', { body: input })
24
33
  }
@@ -37,9 +46,21 @@ export class MeResource {
37
46
  })
38
47
  }
39
48
 
40
- /** The vault the caller belongs to — where most screens start. */
41
- vault(): Promise<unknown> {
42
- return this.#http.request('GET', '/api/v1/me/vault')
49
+ /**
50
+ * The vault the caller belongs to — where most screens start. Carries the
51
+ * derivation-path structure (`routes`, enabled variants only), which is
52
+ * what an address-creation form is built from.
53
+ *
54
+ * ONE vault, even if there are several memberships: the first one found is
55
+ * returned. Membership is a `VaultMembership`, not "has at least one policy
56
+ * group", so somebody with no groups still gets their vault here — they can
57
+ * open the application and will simply be refused every action.
58
+ *
59
+ * No membership anywhere answers 404, which is a legitimate state for a
60
+ * brand-new account rather than an error to report as a fault.
61
+ */
62
+ vault(): Promise<MeVaultResponse> {
63
+ return this.#http.request<MeVaultResponse>('GET', '/api/v1/me/vault')
43
64
  }
44
65
 
45
66
  /**
@@ -60,7 +81,11 @@ export class MeResource {
60
81
  return this.#http.request<PushToken>('POST', '/api/v1/me/push-tokens', { body: input })
61
82
  }
62
83
 
63
- removePushToken(token: string): Promise<unknown> {
64
- return this.#http.request('DELETE', `/api/v1/me/push-tokens/${encodeURIComponent(token)}`)
84
+ /** `deleted: false` simply means the token was not registered — not a failure. */
85
+ removePushToken(token: string): Promise<{ deleted: boolean }> {
86
+ return this.#http.request<{ deleted: boolean }>(
87
+ 'DELETE',
88
+ `/api/v1/me/push-tokens/${encodeURIComponent(token)}`,
89
+ )
65
90
  }
66
91
  }
@@ -1,8 +1,10 @@
1
1
  import { seg, type HttpClient } from '../core/http.js'
2
2
  import type { PageParams } from '../core/pagination.js'
3
3
  import type {
4
+ MemberCard,
4
5
  MemberDetail,
5
6
  MembersPage,
7
+ UserGroup,
6
8
  UserGroupDetail,
7
9
  UserGroupMembers,
8
10
  } from '../generated/types.js'
@@ -43,16 +45,33 @@ export class MembersResource {
43
45
  * By username only — an email address will not resolve. This is the step
44
46
  * before granting access to someone: you need their id, and you can only
45
47
  * resolve people you are allowed to see.
48
+ *
49
+ * A person you cannot see answers 404, the same as one who does not exist.
50
+ * That is deliberate: a distinguishable answer would let anyone probe which
51
+ * usernames are taken. So a 404 here means "no such visible member", and
52
+ * the fix may be a visibility grant rather than a different spelling.
46
53
  */
47
- resolve(username: string): Promise<unknown> {
48
- return this.#http.request('GET', `/api/v1/vaults/${this.#vault}/members/resolve`, {
49
- query: { username },
50
- })
54
+ resolve(username: string): Promise<MemberCard> {
55
+ return this.#http.request<MemberCard>(
56
+ 'GET',
57
+ `/api/v1/vaults/${this.#vault}/members/resolve`,
58
+ { query: { username } },
59
+ )
51
60
  }
52
61
 
53
- /** Policy groups as a member sees them. */
54
- groups(): Promise<unknown> {
55
- return this.#http.request('GET', `/api/v1/vaults/${this.#vault}/user-groups`)
62
+ /**
63
+ * Policy groups as a member sees them: id, name, icon and `memberCount`
64
+ * only — enough to pick one as the subject of a grant. `search` matches the
65
+ * name.
66
+ *
67
+ * No cursor: the whole list comes back at once.
68
+ */
69
+ groups(params: { search?: string | undefined } = {}): Promise<{ items: UserGroup[] }> {
70
+ return this.#http.request<{ items: UserGroup[] }>(
71
+ 'GET',
72
+ `/api/v1/vaults/${this.#vault}/user-groups`,
73
+ { query: { ...params } },
74
+ )
56
75
  }
57
76
 
58
77
  /**
@@ -3,14 +3,40 @@ import type { PageParams } from '../core/pagination.js'
3
3
  import type {
4
4
  ApprovalRelations,
5
5
  CreateOperationRelationBody,
6
+ IncomingTransactionDetail,
6
7
  OperationMetadata,
7
8
  OperationRelation,
8
9
  OperationRelationsPage,
10
+ OperationType,
9
11
  PatchOperationMetadataBody,
10
12
  } from '../generated/types.js'
11
13
 
12
- /** Operation kind in a path: `transfer`, `deposit`, … */
13
- export type OperationKind = string
14
+ /**
15
+ * Operation kind in a path. Exactly two values exist.
16
+ *
17
+ * The document has no schema of this name, so nothing was being shadowed —
18
+ * but `= string` was still wider than the contract, and the endpoints below
19
+ * answer 400 to anything but these two. Kept as a name of its own rather than
20
+ * dropped, because it is exported from the package root; it is now the same
21
+ * type as `OperationType`, which the relation records themselves carry, so
22
+ * the two can be mixed freely.
23
+ */
24
+ export type OperationKind = OperationType
25
+
26
+ export interface ListRelationsParams extends PageParams {
27
+ relationType?: 'refund' | 'reversal' | 'correction' | 'replacement' | undefined
28
+ sourceOperationId?: string | undefined
29
+ targetOperationId?: string | undefined
30
+ /** Links that carry a partial amount in this token. */
31
+ tokenId?: string | undefined
32
+ /**
33
+ * Window over when the LINK was recorded, not over the operations it joins.
34
+ * Half-open: `from` is inclusive, `to` exclusive, so consecutive days can
35
+ * be paged without double-counting the boundary.
36
+ */
37
+ from?: string | undefined
38
+ to?: string | undefined
39
+ }
14
40
 
15
41
  /**
16
42
  * Everything hanging off a single operation: free-form metadata, links to
@@ -69,13 +95,38 @@ export class OperationsResource {
69
95
  * How the operation's status changed, with timestamps. For a transfer this
70
96
  * is the honest record of the path through approval, reservation and
71
97
  * settlement — useful when "why is it still pending" needs an answer.
98
+ *
99
+ * Ordered oldest first. Operations that predate this log have a single
100
+ * baseline row, so one entry does not mean only one transition happened.
101
+ * `changedById` is `null` where no person made the move — the wallet
102
+ * webhook crediting a deposit, a confirmed broadcast, reconciliation by
103
+ * hash — so do not render it as an unknown user. Read `reason` instead; it
104
+ * carries the machine code (`webhook_topup`, `broadcast_confirmed`,
105
+ * `approval_threshold_reached`, …). The row has no schema name of its own
106
+ * in the document, which is why it is spelled out here.
72
107
  */
73
- statusHistory(kind: OperationKind, operationId: string): Promise<unknown> {
108
+ statusHistory(
109
+ kind: OperationKind,
110
+ operationId: string,
111
+ ): Promise<{
112
+ items: Array<{
113
+ id: string
114
+ fromStatus: string | null
115
+ toStatus: string
116
+ reason: string | null
117
+ changedById: string | null
118
+ changedAt: string
119
+ }>
120
+ }> {
74
121
  return this.#http.request('GET', `${this.#op(kind, operationId)}/status-history`)
75
122
  }
76
123
 
77
- /** All links in the vault. */
78
- listRelations(params: PageParams = {}): Promise<OperationRelationsPage> {
124
+ /**
125
+ * All links in the vault. Both directions: an operation appears here when
126
+ * it is the source AND when it is the target, so filtering by
127
+ * `sourceOperationId` alone will miss half of its links.
128
+ */
129
+ listRelations(params: ListRelationsParams = {}): Promise<OperationRelationsPage> {
79
130
  return this.#http.request<OperationRelationsPage>(
80
131
  'GET',
81
132
  `/api/v1/vaults/${this.#vault}/operation-relations`,
@@ -91,9 +142,23 @@ export class OperationsResource {
91
142
  )
92
143
  }
93
144
 
94
- /** An incoming payment in detail. */
95
- deposit(depositId: string): Promise<unknown> {
96
- return this.#http.request('GET', `/api/v1/vaults/${this.#vault}/deposits/${seg(depositId)}`)
145
+ /**
146
+ * An incoming payment in detail.
147
+ *
148
+ * `amlLevel`/`amlScore`/`amlCheckedAt` are a SNAPSHOT of what was known
149
+ * about the SENDER when the money landed, not the sender's risk today. A
150
+ * `null` level means nobody checked — not that there is no risk — so it
151
+ * must not be drawn as a clean result; `/aml/score` answers for the present.
152
+ *
153
+ * `amount` is raw smallest units and `amountDecimal` is the same number
154
+ * scaled by the token's decimals. `usdRate` is the rate at the moment of
155
+ * the deposit and is `null` when no rate was available.
156
+ */
157
+ deposit(depositId: string): Promise<IncomingTransactionDetail> {
158
+ return this.#http.request<IncomingTransactionDetail>(
159
+ 'GET',
160
+ `/api/v1/vaults/${this.#vault}/deposits/${seg(depositId)}`,
161
+ )
97
162
  }
98
163
 
99
164
  /**
@@ -1,5 +1,6 @@
1
1
  import type { HttpClient } from '../core/http.js'
2
2
  import type {
3
+ AmlValue,
3
4
  CreateNetworkBody,
4
5
  CreateTokenBody,
5
6
  EvaluationRequest,
@@ -11,6 +12,38 @@ import type {
11
12
  TokenWithNetwork,
12
13
  } from '../generated/types.js'
13
14
 
15
+ /**
16
+ * The AML risk of one address, as `amlScore` returns it.
17
+ *
18
+ * Hand-written because the API declares this body inline rather than as a
19
+ * named schema, so `src/generated/types.ts` has no name for it. The risk
20
+ * LEVEL is reused from the generated policy selector (`AmlValue.minLevel`):
21
+ * one list, so a sixth level added to the API cannot be read here as five.
22
+ */
23
+ export interface AmlScore {
24
+ address: string
25
+ /** `tron` or `ethereum`, decided by the shape of the address. */
26
+ network: string
27
+ /** 0–10. Do not threshold on it — see `level`. */
28
+ score: number
29
+ /**
30
+ * Branch on THIS, not on `score`: the provider recalibrates its scores and
31
+ * ships its own thresholds with every answer, so a rule written in numbers
32
+ * drifts with their calibration while the level stays put.
33
+ */
34
+ level: AmlValue['minLevel']
35
+ /** Themes of the address's links: `Blacklisted`, `Sanction list`, `Exchange`… */
36
+ categories: Array<{ name: string; count: number }>
37
+ /** Counts of linked entities per level, plus incoming/outgoing/total. */
38
+ summary: Record<string, number> | null
39
+ /** When the PROVIDER ran the check. */
40
+ checkedAt: string
41
+ /** When we received it. With `cached: true` this is the age of the cache entry. */
42
+ fetchedAt: string
43
+ /** `true` — served from the 24h cache, no external call was made. */
44
+ cached: boolean
45
+ }
46
+
14
47
  /**
15
48
  * Registries and preflight checks. Not scoped to a vault.
16
49
  *
@@ -34,9 +67,20 @@ export class RegistriesResource {
34
67
  return this.#http.request<TokenWithNetwork[]>('GET', '/api/v1/tokens', { query: { ...params } })
35
68
  }
36
69
 
37
- /** USD rates used to value operations. */
38
- rates(): Promise<RatesResponse> {
39
- return this.#http.request<RatesResponse>('GET', '/api/v1/rates')
70
+ /**
71
+ * USD rates used to value operations.
72
+ *
73
+ * `codes` narrows the answer to the tokens you name; without it every
74
+ * enabled token of the registry comes back. The parameter goes out as one
75
+ * comma-separated value, which is what the endpoint parses — an array is
76
+ * joined for you.
77
+ *
78
+ * Rates are fetched live on every call and NOT cached, so this is the
79
+ * "right now" figure. The rate stored on an operation is a separate thing:
80
+ * a snapshot taken when it was created.
81
+ */
82
+ rates(params: { codes?: string | readonly string[] | undefined } = {}): Promise<RatesResponse> {
83
+ return this.#http.request<RatesResponse>('GET', '/api/v1/rates', { query: { ...params } })
40
84
  }
41
85
 
42
86
  /**
@@ -51,20 +95,55 @@ export class RegistriesResource {
51
95
  * • it has no verdict for "there is nobody to sign". A chain that cannot
52
96
  * be satisfied still previews as `requireApproval`, and the refusal
53
97
  * arrives only when the request is actually created.
98
+ *
99
+ * `explain: true` adds a per-policy TRACE to the answer — why each policy
100
+ * did or did not apply, the main tool for debugging a rule set. All four
101
+ * decisions carry it then, a bare `deny` included, which is where it earns
102
+ * its keep: one entry per ENABLED policy the person has for this action,
103
+ * saying whether it applied and, for one that did not, the selector it
104
+ * failed on when one can be named. Somebody with no such policy gets an
105
+ * empty array rather than no key.
106
+ *
107
+ * Without the flag there is no `trace` at all. `EvaluationResult` keeps the
108
+ * field optional on every branch for that reason: the document describes
109
+ * both modes with one schema. The verdict itself is computed the same way
110
+ * either way.
54
111
  */
55
- evaluate(input: EvaluationRequest): Promise<EvaluationResult> {
56
- return this.#http.request<EvaluationResult>('POST', '/api/v1/evaluate', { body: input })
112
+ evaluate(
113
+ input: EvaluationRequest,
114
+ options: { explain?: boolean | undefined } = {},
115
+ ): Promise<EvaluationResult> {
116
+ return this.#http.request<EvaluationResult>('POST', '/api/v1/evaluate', {
117
+ body: input,
118
+ query: { ...options },
119
+ })
57
120
  }
58
121
 
59
122
  /**
60
- * Current AML risk of an address.
123
+ * Current AML risk of an address. Call it BEFORE sending: the transfer form
124
+ * should show the recipient's risk before the button is pressed, not after.
125
+ *
126
+ * "Not checked" is NOT a value of `level` here. This endpoint either
127
+ * answers with a level or fails — 502 `AML_UNAVAILABLE` when the provider
128
+ * times out, 5xxs, or has no key configured, and 400 when the address
129
+ * matches neither the TRON nor the Ethereum shape. Treat the 502 as
130
+ * "could not check", never as a green badge; the `null` level that means
131
+ * "nobody ever checked" lives on OPERATIONS, not on this answer.
61
132
  *
62
- * A missing level means NOT CHECKED, not "no risk" — a green badge on an
63
- * unchecked address misleads about money. The level, not the numeric score,
64
- * is the stable contract: providers recalibrate scores.
133
+ * What comes back is a snapshot cached for 24 hours — `cached` says whether
134
+ * an external call happened. `refresh` forces a fresh one and is the only
135
+ * way past the cache, so it belongs on an explicit user action ("check
136
+ * again"): it is slower and costs a provider call every time.
137
+ *
138
+ * Note what this is NOT: the risk recorded on a transfer or a deposit is a
139
+ * separate snapshot taken at the moment of the operation. This figure moves
140
+ * over time, so it will disagree with the one stored on an old operation,
141
+ * and that is the point of storing it there.
65
142
  */
66
- amlScore(address: string): Promise<unknown> {
67
- return this.#http.request('GET', '/api/v1/aml/score', { query: { address } })
143
+ amlScore(address: string, options: { refresh?: boolean | undefined } = {}): Promise<AmlScore> {
144
+ return this.#http.request<AmlScore>('GET', '/api/v1/aml/score', {
145
+ query: { address, ...options },
146
+ })
68
147
  }
69
148
 
70
149
  /**