@firela/api-types 0.0.0-canary.8a8b3972 → 0.0.0-canary.8bf286b3

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.
@@ -6,18 +6,12 @@ export const $CreateAccountDto = {
6
6
  path: {
7
7
  type: 'string',
8
8
  description: 'Account path (hierarchical, colon-separated)',
9
- example: 'Assets:CN:Bank:ICBC:Checking'
10
- },
11
- displayName: {
12
- type: 'string',
13
- description:
14
- 'Display name to distinguish accounts at the same path (default: "")',
15
- example: '工资卡'
9
+ example: 'Assets:CN:ICBC:Checking'
16
10
  },
17
11
  openDate: {
18
12
  format: 'date-time',
19
13
  type: 'string',
20
- description: 'Account open date',
14
+ description: 'Account open date (server defaults to today)',
21
15
  example: '2024-01-01'
22
16
  },
23
17
  currencies: {
@@ -45,26 +39,22 @@ export const $CreateAccountDto = {
45
39
  templatePath: {
46
40
  type: 'string',
47
41
  description: 'Reference to account-standards template path',
48
- example: 'Assets:CN:Bank:ICBC:Checking'
42
+ example: 'Assets:CN:Checking'
49
43
  },
50
44
  isCustom: {
51
45
  type: 'boolean',
52
46
  description: 'Whether this is a custom (user-created) account',
53
47
  default: false
54
48
  },
55
- i18nKey: {
56
- type: 'string',
57
- description: 'i18n key for display name (overrides template)',
58
- example: 'account.custom.mybank'
59
- },
60
49
  icon: {
61
50
  type: 'string',
62
51
  description: 'Icon identifier (overrides template)',
63
52
  example: 'bank-custom'
64
53
  },
65
- openMeta: {
54
+ openDirectiveMeta: {
66
55
  type: 'object',
67
- description: 'Additional metadata',
56
+ description:
57
+ 'Open directive metadata (NOT an opening-balance amount — use the opening-balance endpoint)',
68
58
  example: {
69
59
  branch: 'Downtown',
70
60
  accountNumber: '1234'
@@ -76,7 +66,7 @@ export const $CreateAccountDto = {
76
66
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
77
67
  }
78
68
  },
79
- required: ['path', 'openDate']
69
+ required: ['path']
80
70
  } as const;
81
71
 
82
72
  export const $AccountResponseDto = {
@@ -90,13 +80,7 @@ export const $AccountResponseDto = {
90
80
  path: {
91
81
  type: 'string',
92
82
  description: 'Account path (hierarchical, colon-separated)',
93
- example: 'Assets:CN:Bank:ICBC:Checking'
94
- },
95
- displayName: {
96
- type: 'string',
97
- description:
98
- 'Display name distinguishing multiple accounts at the same path',
99
- example: '工资卡'
83
+ example: 'Assets:CN:ICBC:Checking'
100
84
  },
101
85
  type: {
102
86
  type: 'string',
@@ -104,6 +88,49 @@ export const $AccountResponseDto = {
104
88
  enum: ['Assets', 'Liabilities', 'Income', 'Expenses', 'Equity'],
105
89
  example: 'Assets'
106
90
  },
91
+ assetSubClass: {
92
+ type: 'string',
93
+ description:
94
+ 'Account-level asset sub-class (product type, e.g. STOCK/DEPOSIT/CREDIT_CARD/PERSONAL_LOAN). Computed from the account path via the asset-classifier (ADR-0077). Null for non-asset accounts (Income/Expenses/Equity) or unmatched paths.',
95
+ enum: [
96
+ 'DEPOSIT',
97
+ 'CASH',
98
+ 'MONEY_MARKET_FUND',
99
+ 'STOCK',
100
+ 'ETF',
101
+ 'MUTUAL_FUND',
102
+ 'EQUITY_COMPENSATION',
103
+ 'GOVERNMENT_BOND',
104
+ 'CORPORATE_BOND',
105
+ 'BOND_FUND',
106
+ 'PRIMARY_RESIDENCE',
107
+ 'INVESTMENT_PROPERTY',
108
+ 'REIT',
109
+ 'GOLD',
110
+ 'SILVER',
111
+ 'PRECIOUS_METAL',
112
+ 'PRECIOUS_METAL_FUND',
113
+ 'COMMODITY',
114
+ 'COMMODITY_FUND',
115
+ 'CRYPTOCURRENCY',
116
+ 'RETIREMENT_ACCOUNT',
117
+ 'HEALTH_ACCOUNT',
118
+ 'EDUCATION_ACCOUNT',
119
+ 'INSURANCE',
120
+ 'PRIVATE_EQUITY',
121
+ 'HEDGE_FUND',
122
+ 'COLLECTIBLES',
123
+ 'MORTGAGE',
124
+ 'STUDENT_LOAN',
125
+ 'CREDIT_CARD',
126
+ 'PERSONAL_LOAN',
127
+ 'ACCOUNTS_PAYABLE',
128
+ 'TAX_PAYABLE',
129
+ 'OTHER'
130
+ ],
131
+ nullable: true,
132
+ example: 'STOCK'
133
+ },
107
134
  status: {
108
135
  type: 'string',
109
136
  description: 'Account status',
@@ -145,26 +172,26 @@ export const $AccountResponseDto = {
145
172
  templatePath: {
146
173
  type: 'string',
147
174
  description: 'Template path reference',
148
- example: 'Assets:CN:Bank:ICBC:Checking'
175
+ example: 'Assets:CN:Checking'
149
176
  },
150
177
  isCustom: {
151
178
  type: 'boolean',
152
179
  description: 'Whether this is a custom (user-created) account',
153
180
  example: false
154
181
  },
155
- i18nKey: {
182
+ displayName: {
156
183
  type: 'string',
157
- description: 'i18n key for display name',
158
- example: 'account.assets.cn.bank.icbc.checking'
184
+ description: 'Localized display name (ADR-0114, read-time projection)',
185
+ example: 'Checking'
159
186
  },
160
187
  icon: {
161
188
  type: 'string',
162
189
  description: 'Icon identifier',
163
190
  example: 'bank-icbc'
164
191
  },
165
- openMeta: {
192
+ openDirectiveMeta: {
166
193
  type: 'object',
167
- description: 'Account metadata',
194
+ description: 'Open directive metadata (ADR-0115 Decision 9)',
168
195
  example: {
169
196
  branch: 'Downtown'
170
197
  }
@@ -192,7 +219,6 @@ export const $AccountResponseDto = {
192
219
  required: [
193
220
  'id',
194
221
  'path',
195
- 'displayName',
196
222
  'type',
197
223
  'status',
198
224
  'openDate',
@@ -225,11 +251,6 @@ export const $AccountListResponseDto = {
225
251
  export const $UpdateAccountDto = {
226
252
  type: 'object',
227
253
  properties: {
228
- displayName: {
229
- type: 'string',
230
- description: 'Display name to distinguish accounts at the same path',
231
- example: '招行工资卡'
232
- },
233
254
  currencies: {
234
255
  description: 'Allowed currencies (null = no restriction)',
235
256
  example: ['CNY', 'USD'],
@@ -251,19 +272,15 @@ export const $UpdateAccountDto = {
251
272
  'NONE'
252
273
  ]
253
274
  },
254
- i18nKey: {
255
- type: 'string',
256
- description: 'i18n key for display name',
257
- example: 'account.custom.mybank'
258
- },
259
275
  icon: {
260
276
  type: 'string',
261
277
  description: 'Icon identifier',
262
278
  example: 'bank-custom'
263
279
  },
264
- openMeta: {
280
+ openDirectiveMeta: {
265
281
  type: 'object',
266
- description: 'Additional metadata (merged with existing)',
282
+ description:
283
+ 'Open directive metadata (merged with existing; NOT an opening-balance amount)',
267
284
  example: {
268
285
  branch: 'Uptown'
269
286
  }
@@ -310,13 +327,47 @@ export const $ReopenAccountDto = {
310
327
  }
311
328
  } as const;
312
329
 
330
+ export const $CreateOpeningBalanceDto = {
331
+ type: 'object',
332
+ properties: {
333
+ amount: {
334
+ type: 'number',
335
+ description: 'Opening balance amount (non-negative)',
336
+ example: 1000
337
+ },
338
+ currency: {
339
+ type: 'string',
340
+ description: 'Currency code',
341
+ example: 'CNY'
342
+ },
343
+ date: {
344
+ format: 'date-time',
345
+ type: 'string',
346
+ description: 'Opening-balance date (defaults to now)',
347
+ example: '2024-01-01'
348
+ }
349
+ },
350
+ required: ['amount', 'currency']
351
+ } as const;
352
+
353
+ export const $OpeningBalanceResultDto = {
354
+ type: 'object',
355
+ properties: {
356
+ transactionId: {
357
+ type: 'string',
358
+ description: 'Created opening-balance transaction id.'
359
+ }
360
+ },
361
+ required: ['transactionId']
362
+ } as const;
363
+
313
364
  export const $AccountStandardResponseDto = {
314
365
  type: 'object',
315
366
  properties: {
316
367
  path: {
317
368
  type: 'string',
318
369
  description: 'Account path (hierarchical, colon-separated)',
319
- example: 'Assets:CN:Bank:ICBC:Checking'
370
+ example: 'Assets:CN:Checking'
320
371
  },
321
372
  type: {
322
373
  type: 'string',
@@ -324,11 +375,6 @@ export const $AccountStandardResponseDto = {
324
375
  enum: ['Assets', 'Liabilities', 'Income', 'Expenses', 'Equity'],
325
376
  example: 'Assets'
326
377
  },
327
- i18nKey: {
328
- type: 'string',
329
- description: 'i18n key for localized display name',
330
- example: 'account.assets.cn.bank.icbc.checking'
331
- },
332
378
  name: {
333
379
  type: 'string',
334
380
  description: 'Short localized display name',
@@ -351,9 +397,47 @@ export const $AccountStandardResponseDto = {
351
397
  type: 'string',
352
398
  description: 'Icon identifier for UI display',
353
399
  example: 'bank-icbc'
400
+ },
401
+ productCategory: {
402
+ type: 'string',
403
+ description:
404
+ 'Onboarding product category (coarse grouping derived from assetSubClass)',
405
+ enum: [
406
+ 'cash',
407
+ 'investment',
408
+ 'credit_card',
409
+ 'loan',
410
+ 'payable_tax',
411
+ 'other'
412
+ ],
413
+ example: 'investment'
414
+ },
415
+ assetClass: {
416
+ type: 'string',
417
+ description:
418
+ 'Asset class (LIQUIDITY/EQUITY/.../LIABILITY), derived at read time from classification rules',
419
+ enum: [
420
+ 'LIQUIDITY',
421
+ 'EQUITY',
422
+ 'FIXED_INCOME',
423
+ 'PRECIOUS_METALS',
424
+ 'COMMODITY',
425
+ 'INSURANCE',
426
+ 'ALTERNATIVE_INVESTMENT',
427
+ 'PERSONAL_ASSETS',
428
+ 'LIABILITY',
429
+ 'REAL_ESTATE',
430
+ 'INDEX'
431
+ ]
432
+ },
433
+ assetSubClass: {
434
+ type: 'string',
435
+ description:
436
+ 'Asset sub-class (product type, derived at read time from classification rules)',
437
+ example: 'STOCK'
354
438
  }
355
439
  },
356
- required: ['path', 'type', 'i18nKey', 'description', 'tags', 'icon']
440
+ required: ['path', 'type', 'description', 'tags', 'icon', 'productCategory']
357
441
  } as const;
358
442
 
359
443
  export const $AccountStandardListResponseDto = {
@@ -383,18 +467,13 @@ export const $AccountStandardListResponseDto = {
383
467
  export const $TemplateMetadataDto = {
384
468
  type: 'object',
385
469
  properties: {
386
- extendable: {
387
- type: 'boolean',
388
- description: 'Whether this path can be extended',
389
- example: true
390
- },
391
470
  rootType: {
392
471
  type: 'string',
393
472
  description: 'Root account type',
394
473
  example: 'Assets'
395
474
  }
396
475
  },
397
- required: ['extendable', 'rootType']
476
+ required: ['rootType']
398
477
  } as const;
399
478
 
400
479
  export const $TemplateMetadataResponseDto = {
@@ -533,7 +612,7 @@ export const $CreatePostingDto = {
533
612
  type: 'string',
534
613
  description:
535
614
  'Account name in Beancount format (must start with uppercase, colon-separated)',
536
- example: 'Assets:Bank:Checking'
615
+ example: 'Assets:Checking'
537
616
  },
538
617
  units: {
539
618
  type: 'string',
@@ -660,24 +739,59 @@ export const $CreateTransactionDto = {
660
739
  required: ['date', 'narration', 'postings']
661
740
  } as const;
662
741
 
742
+ export const $CostDetailDto = {
743
+ type: 'object',
744
+ properties: {
745
+ number: {
746
+ type: 'string',
747
+ description: 'Per-unit cost basis (mirrors engine Cost.number)',
748
+ example: '240'
749
+ },
750
+ currency: {
751
+ type: 'string',
752
+ description: 'Cost currency',
753
+ example: 'USD'
754
+ },
755
+ date: {
756
+ type: 'string',
757
+ description: 'Lot acquisition date (ISO yyyy-mm-dd)',
758
+ example: '2024-01-15'
759
+ },
760
+ label: {
761
+ type: 'string',
762
+ description: 'Lot label',
763
+ example: 'lot-2024-01'
764
+ }
765
+ }
766
+ } as const;
767
+
663
768
  export const $PostingResponseDto = {
664
769
  type: 'object',
665
770
  properties: {
666
771
  account: {
667
772
  type: 'string',
668
773
  description: 'Account name',
669
- example: 'Assets:Bank:Checking'
774
+ example: 'Assets:Checking'
670
775
  },
671
776
  units: {
672
777
  type: 'string',
673
778
  description:
674
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
779
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
675
780
  example: '100.50'
676
781
  },
677
782
  currency: {
678
783
  type: 'string',
679
784
  description: 'Currency',
680
785
  example: 'USD'
786
+ },
787
+ cost: {
788
+ description:
789
+ 'Booking-resolved cost (mirrors engine Cost). Undefined when the posting has no cost basis.',
790
+ allOf: [
791
+ {
792
+ $ref: '#/components/schemas/CostDetailDto'
793
+ }
794
+ ]
681
795
  }
682
796
  },
683
797
  required: ['account']
@@ -1023,12 +1137,12 @@ export const $PostingDetailDto = {
1023
1137
  account: {
1024
1138
  type: 'string',
1025
1139
  description: 'Fully-qualified Beancount account path',
1026
- example: 'Assets:Bank:Checking'
1140
+ example: 'Assets:Checking'
1027
1141
  },
1028
1142
  units: {
1029
1143
  type: 'string',
1030
1144
  description:
1031
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
1145
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
1032
1146
  example: '100.50'
1033
1147
  },
1034
1148
  currency: {
@@ -1051,6 +1165,15 @@ export const $PostingDetailDto = {
1051
1165
  description: 'Cost date',
1052
1166
  example: '2024-01-15'
1053
1167
  },
1168
+ cost: {
1169
+ description:
1170
+ 'Booking-resolved cost (mirrors engine Cost). Undefined when the posting has no cost basis.',
1171
+ allOf: [
1172
+ {
1173
+ $ref: '#/components/schemas/CostDetailDto'
1174
+ }
1175
+ ]
1176
+ },
1054
1177
  priceAmount: {
1055
1178
  type: 'string',
1056
1179
  description: 'Price amount',
@@ -1203,72 +1326,22 @@ export const $TransactionDetailDto = {
1203
1326
  ]
1204
1327
  } as const;
1205
1328
 
1206
- export const $TransactionListResponseDto = {
1329
+ export const $TransactionListItemDto = {
1207
1330
  type: 'object',
1208
1331
  properties: {
1209
- data: {
1210
- description: 'List of transactions',
1211
- type: 'array',
1212
- items: {
1213
- $ref: '#/components/schemas/TransactionDetailDto'
1214
- }
1215
- },
1216
- total: {
1217
- type: 'number',
1218
- description: 'Total count of matching transactions',
1219
- example: 100
1220
- },
1221
- limit: {
1222
- type: 'number',
1223
- description: 'Number of items per page',
1224
- example: 20
1332
+ id: {
1333
+ type: 'string',
1334
+ description: 'Transaction ID',
1335
+ example: 'clh1234567890abcdef'
1225
1336
  },
1226
- offset: {
1227
- type: 'number',
1228
- description: 'Number of items skipped',
1229
- example: 0
1230
- }
1231
- },
1232
- required: ['data', 'total', 'limit', 'offset']
1233
- } as const;
1234
-
1235
- export const $TagSuggestionDto = {
1236
- type: 'object',
1237
- properties: {
1238
- tag: {
1337
+ date: {
1239
1338
  type: 'string',
1240
- description: 'Tag name',
1241
- example: 'Monthly'
1339
+ description: 'Transaction date',
1340
+ example: '2024-11-28'
1242
1341
  },
1243
- count: {
1244
- type: 'number',
1245
- description: 'Usage count across ACTIVE transactions',
1246
- example: 12
1247
- }
1248
- },
1249
- required: ['tag', 'count']
1250
- } as const;
1251
-
1252
- export const $TagSuggestionsResponseDto = {
1253
- type: 'object',
1254
- properties: {
1255
- data: {
1256
- description: 'Tag suggestions sorted as requested',
1257
- type: 'array',
1258
- items: {
1259
- $ref: '#/components/schemas/TagSuggestionDto'
1260
- }
1261
- }
1262
- },
1263
- required: ['data']
1264
- } as const;
1265
-
1266
- export const $UpdateTransactionDto = {
1267
- type: 'object',
1268
- properties: {
1269
1342
  flag: {
1270
1343
  type: 'string',
1271
- description: 'Transaction flag (CLEARED, PENDING, etc.)',
1344
+ description: 'Transaction flag',
1272
1345
  enum: [
1273
1346
  'CLEARED',
1274
1347
  'PENDING',
@@ -1279,22 +1352,24 @@ export const $UpdateTransactionDto = {
1279
1352
  ],
1280
1353
  example: 'CLEARED'
1281
1354
  },
1355
+ customFlag: {
1356
+ type: 'string',
1357
+ description: 'Custom flag (if not using standard flags)',
1358
+ example: 'R'
1359
+ },
1282
1360
  payee: {
1283
1361
  type: 'string',
1284
1362
  description: 'Payee name',
1285
- maxLength: 500,
1286
1363
  example: 'Whole Foods Market'
1287
1364
  },
1288
1365
  narration: {
1289
1366
  type: 'string',
1290
- description: 'Transaction narration/description',
1291
- maxLength: 1000,
1292
- example: 'Weekly grocery shopping'
1367
+ description: 'Transaction narration',
1368
+ example: 'Grocery shopping'
1293
1369
  },
1294
1370
  tags: {
1295
1371
  description: 'Transaction tags',
1296
- maxItems: 50,
1297
- example: ['groceries', 'weekly'],
1372
+ example: ['groceries'],
1298
1373
  type: 'array',
1299
1374
  items: {
1300
1375
  type: 'string'
@@ -1302,7 +1377,6 @@ export const $UpdateTransactionDto = {
1302
1377
  },
1303
1378
  links: {
1304
1379
  description: 'Transaction links',
1305
- maxItems: 50,
1306
1380
  example: ['invoice-2024-001'],
1307
1381
  type: 'array',
1308
1382
  items: {
@@ -1311,320 +1385,420 @@ export const $UpdateTransactionDto = {
1311
1385
  },
1312
1386
  meta: {
1313
1387
  type: 'object',
1314
- description: 'Transaction metadata (JSON object)',
1315
- example: {
1316
- category: 'Food:Groceries',
1317
- reviewed: true
1318
- }
1319
- }
1320
- }
1321
- } as const;
1322
-
1323
- export const $BalanceResponseDto = {
1324
- type: 'object',
1325
- properties: {
1326
- account: {
1388
+ description: 'Transaction metadata'
1389
+ },
1390
+ status: {
1327
1391
  type: 'string',
1328
- description: 'Account name',
1329
- example: 'Assets:Bank:Checking'
1392
+ description: 'Transaction status',
1393
+ enum: ['ACTIVE', 'VOIDED', 'SUPERSEDED'],
1394
+ example: 'ACTIVE'
1330
1395
  },
1331
- balance: {
1396
+ sourceType: {
1332
1397
  type: 'string',
1333
- description: 'Balance amount (decimal string for precision)',
1334
- example: '12345.67'
1398
+ description:
1399
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1335
1400
  },
1336
- currency: {
1401
+ sourcePlatform: {
1337
1402
  type: 'string',
1338
- description: 'Currency code',
1339
- example: 'USD'
1403
+ description: 'Source platform (e.g., alipay, wechat)',
1404
+ example: 'alipay'
1340
1405
  },
1341
- date: {
1406
+ postings: {
1407
+ description: 'Transaction postings',
1408
+ type: 'array',
1409
+ items: {
1410
+ $ref: '#/components/schemas/PostingDetailDto'
1411
+ }
1412
+ },
1413
+ createdAt: {
1342
1414
  type: 'string',
1343
- description: 'Date of the balance calculation (ISO 8601)',
1344
- example: '2024-12-31T00:00:00.000Z'
1345
- }
1346
- },
1347
- required: ['account', 'balance', 'currency', 'date']
1348
- } as const;
1349
-
1350
- export const $MultiCurrencyBalanceResponseDto = {
1351
- type: 'object',
1352
- properties: {
1353
- account: {
1354
- type: 'string',
1355
- description: 'Account name',
1356
- example: 'Assets:Bank:Checking'
1357
- },
1358
- balances: {
1359
- type: 'object',
1360
- description: 'Balances by currency',
1361
- example: {
1362
- USD: '12345.67',
1363
- CNY: '100000.00'
1364
- }
1365
- },
1366
- date: {
1367
- type: 'string',
1368
- description: 'Date of the balance calculation (ISO 8601)',
1369
- example: '2024-12-31T00:00:00.000Z'
1370
- }
1371
- },
1372
- required: ['account', 'balances', 'date']
1373
- } as const;
1374
-
1375
- export const $TransactionSummaryDto = {
1376
- type: 'object',
1377
- properties: {
1378
- id: {
1379
- type: 'string',
1380
- description: 'Transaction ID (null if transaction deleted)',
1381
- example: 'clh1234567890abcdef',
1382
- nullable: true
1383
- },
1384
- date: {
1385
- type: 'string',
1386
- description: 'Transaction date (YYYY-MM-DD)',
1387
- example: '2024-03-15'
1415
+ description: 'Created at timestamp',
1416
+ example: '2024-11-28T10:30:00.000Z'
1388
1417
  },
1389
- amount: {
1418
+ voidedAt: {
1390
1419
  type: 'string',
1391
- description: 'Transaction amount (absolute value)',
1392
- example: '128.50'
1420
+ description: 'Voided at timestamp (if voided)',
1421
+ example: '2024-11-29T15:00:00.000Z'
1393
1422
  },
1394
- currency: {
1423
+ voidedBy: {
1395
1424
  type: 'string',
1396
- description: 'Currency code',
1397
- example: 'CNY'
1425
+ description: 'User ID who voided this transaction',
1426
+ example: 'clh1234567890abcdef'
1398
1427
  },
1399
- payee: {
1428
+ correctionReason: {
1400
1429
  type: 'string',
1401
- description: 'Payee/Merchant name',
1402
- example: 'Starbucks'
1430
+ description: 'Correction reason (if voided or superseded)',
1431
+ example: 'Duplicate entry'
1403
1432
  },
1404
- narration: {
1433
+ supersededBy: {
1405
1434
  type: 'string',
1406
- description: 'Transaction narration',
1407
- example: 'Coffee purchase'
1435
+ description:
1436
+ 'ID of the transaction that supersedes this one (set when status=SUPERSEDED)',
1437
+ example: 'clh1234567890abcdef'
1408
1438
  },
1409
- accountName: {
1439
+ originalTxn: {
1410
1440
  type: 'string',
1411
- description: 'Source account name (first posting)',
1412
- example: 'Assets:Bank:Checking'
1441
+ description:
1442
+ 'ID of the transaction this one corrected/replaced (back-link on the replacement)',
1443
+ example: 'clh1234567890abcdef'
1413
1444
  },
1414
- sourceType: {
1445
+ viewpointAmount: {
1415
1446
  type: 'string',
1416
1447
  description:
1417
- 'Source type (free-form string from transaction metadata, e.g. import, api)'
1448
+ 'Per-leg sign-normalized row amount for the category viewpoint (ADR-0126): each posting on the category account set contributes its unitsNumber with Income-root legs negated and Expenses-root legs identity. Positive under normal booking but NOT clamped (explicit negative expense legs and net-flip refund months stay negative). Omitted outside the category viewpoint.',
1449
+ example: '10000.00'
1418
1450
  },
1419
- sourcePlatform: {
1451
+ viewpointCurrency: {
1420
1452
  type: 'string',
1421
- description: 'Source platform (e.g., alipay, wechat)',
1422
- example: 'alipay'
1453
+ description:
1454
+ 'Currency of viewpointAmount. A row spanning multiple currencies takes the largest-magnitude currency group (known simplification, ADR-0126). Omitted outside the category viewpoint.',
1455
+ example: 'CNY'
1423
1456
  }
1424
1457
  },
1425
- required: ['date', 'amount', 'currency', 'narration']
1458
+ required: [
1459
+ 'id',
1460
+ 'date',
1461
+ 'narration',
1462
+ 'tags',
1463
+ 'links',
1464
+ 'status',
1465
+ 'postings',
1466
+ 'createdAt'
1467
+ ]
1426
1468
  } as const;
1427
1469
 
1428
- export const $ReviewSummaryDto = {
1470
+ export const $BalanceByCurrencyDto = {
1429
1471
  type: 'object',
1430
1472
  properties: {
1431
- id: {
1473
+ currency: {
1432
1474
  type: 'string',
1433
- description: 'Review item ID'
1475
+ description: 'ISO 4217 currency code',
1476
+ example: 'CNY'
1434
1477
  },
1478
+ balance: {
1479
+ type: 'string',
1480
+ description: 'Balance amount',
1481
+ example: '50000.00'
1482
+ }
1483
+ },
1484
+ required: ['currency', 'balance']
1485
+ } as const;
1486
+
1487
+ export const $ExchangeRateWarningDto = {
1488
+ type: 'object',
1489
+ properties: {
1435
1490
  type: {
1436
1491
  type: 'string',
1437
- description: 'Review type',
1438
- enum: [
1439
- 'DUPLICATE',
1440
- 'RULE_MATCH',
1441
- 'PAYEE_MATCH',
1442
- 'ACCOUNT_VALIDATION',
1443
- 'PIPELINE_ERROR'
1444
- ]
1492
+ description: 'Warning type',
1493
+ example: 'MISSING_EXCHANGE_RATE'
1445
1494
  },
1446
- status: {
1495
+ currency: {
1447
1496
  type: 'string',
1448
- description: 'Review status',
1449
- enum: ['PENDING', 'RESOLVED', 'EXPIRED', 'CANCELLED']
1450
- },
1451
- confidence: {
1452
- type: 'number',
1453
- description: 'Confidence score (0-1)'
1497
+ description: 'Currency without exchange rate',
1498
+ example: 'EUR'
1454
1499
  },
1455
- confidenceLevel: {
1500
+ totalAmount: {
1501
+ type: 'string',
1502
+ description: 'Total amount affected',
1503
+ example: '1000.00'
1504
+ }
1505
+ },
1506
+ required: ['type', 'currency', 'totalAmount']
1507
+ } as const;
1508
+
1509
+ export const $TransactionListSummaryDto = {
1510
+ type: 'object',
1511
+ properties: {
1512
+ totalAmount: {
1456
1513
  type: 'string',
1457
1514
  description:
1458
- 'Confidence level derived from score. Null for error-type reviews (ACCOUNT_VALIDATION/PIPELINE_ERROR) which carry no confidence.',
1459
- enum: ['HIGH', 'MEDIUM', 'LOW'],
1460
- nullable: true
1515
+ 'Partial converted total in base currency (rated currencies only). Sign by viewpoint (ADR-0126): account viewpoint keeps the raw Beancount sign (income negative); category viewpoint is per-leg sign-normalized (Income legs negated, Expenses legs identity — positive under normal booking, not clamped). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1516
+ example: '-6000.00'
1461
1517
  },
1462
- summaryKey: {
1518
+ currency: {
1463
1519
  type: 'string',
1464
- description:
1465
- 'i18n message key for summary (e.g., review.summary.duplicate). Translate on frontend with summaryParams.'
1520
+ description: 'Base currency (ISO 4217)',
1521
+ example: 'CNY'
1466
1522
  },
1467
- summaryParams: {
1468
- type: 'object',
1469
- description:
1470
- 'Parameters for summary message interpolation (e.g., { date: "2024-01-15", amount: "50" })',
1471
- additionalProperties: {
1472
- type: 'string'
1523
+ balanceByCurrency: {
1524
+ description: 'Raw (unconverted) balance per currency',
1525
+ type: 'array',
1526
+ items: {
1527
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
1473
1528
  }
1474
1529
  },
1475
- matchReasons: {
1476
- description: 'Human-readable reasons for branching',
1530
+ warnings: {
1531
+ description: 'Currencies missing an FX rate (omitted when empty)',
1477
1532
  type: 'array',
1478
1533
  items: {
1479
- type: 'string'
1534
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
1480
1535
  }
1481
- },
1482
- sourceType: {
1483
- type: 'string',
1484
- description:
1485
- 'Source type (free-form string from transaction metadata, e.g. import, api)'
1486
- },
1487
- sourcePlatform: {
1488
- type: 'string',
1489
- description: 'Source platform (e.g., alipay, wechat)'
1490
- },
1491
- createdAt: {
1492
- format: 'date-time',
1536
+ }
1537
+ },
1538
+ required: ['totalAmount', 'currency', 'balanceByCurrency']
1539
+ } as const;
1540
+
1541
+ export const $TransactionListViewpointDto = {
1542
+ type: 'object',
1543
+ properties: {
1544
+ type: {
1493
1545
  type: 'string',
1494
- description: 'Creation timestamp'
1495
- },
1496
- transaction: {
1497
1546
  description:
1498
- 'Transaction summary for display (null if transaction deleted)',
1499
- nullable: true,
1500
- allOf: [
1501
- {
1502
- $ref: '#/components/schemas/TransactionSummaryDto'
1503
- }
1504
- ]
1505
- },
1506
- amount: {
1507
- type: 'string',
1508
- description: 'Transaction amount (convenience field for mobile display)'
1509
- },
1510
- currency: {
1511
- type: 'string',
1512
- description: 'Currency code (convenience field for mobile display)'
1513
- },
1514
- merchantName: {
1515
- type: 'string',
1516
- description: 'Payee/Merchant name (convenience field for mobile display)'
1547
+ 'Viewpoint type (only category drill-down carries a viewpoint today)',
1548
+ enum: ['category'],
1549
+ example: 'category'
1517
1550
  },
1518
- accountName: {
1519
- type: 'string',
1520
- description: 'Account name (convenience field for mobile display)'
1521
- },
1522
- transactionTime: {
1551
+ flow: {
1523
1552
  type: 'string',
1524
- description:
1525
- 'Transaction date/time (convenience field for mobile display)'
1553
+ description: 'Flow root the category account set is restricted to',
1554
+ enum: ['income', 'expense'],
1555
+ example: 'expense'
1526
1556
  }
1527
1557
  },
1528
- required: [
1529
- 'id',
1530
- 'type',
1531
- 'status',
1532
- 'confidence',
1533
- 'confidenceLevel',
1534
- 'summaryKey',
1535
- 'matchReasons',
1536
- 'sourceType',
1537
- 'createdAt'
1538
- ]
1558
+ required: ['type', 'flow']
1539
1559
  } as const;
1540
1560
 
1541
- export const $ReviewListResponseDto = {
1561
+ export const $TransactionListResponseDto = {
1542
1562
  type: 'object',
1543
1563
  properties: {
1544
- items: {
1564
+ data: {
1565
+ description: 'List of transactions',
1545
1566
  type: 'array',
1546
1567
  items: {
1547
- $ref: '#/components/schemas/ReviewSummaryDto'
1568
+ $ref: '#/components/schemas/TransactionListItemDto'
1548
1569
  }
1549
1570
  },
1550
1571
  total: {
1551
1572
  type: 'number',
1552
- description: 'Total number of items'
1553
- },
1554
- page: {
1555
- type: 'number',
1556
- description: 'Current page number'
1573
+ description: 'Total count of matching transactions',
1574
+ example: 100
1557
1575
  },
1558
1576
  limit: {
1559
1577
  type: 'number',
1560
- description: 'Items per page'
1578
+ description: 'Number of items per page',
1579
+ example: 20
1561
1580
  },
1562
- hasMore: {
1563
- type: 'boolean',
1564
- description: 'Whether there are more pages'
1565
- }
1566
- },
1567
- required: ['items', 'total', 'page', 'limit', 'hasMore']
1568
- } as const;
1569
-
1570
- export const $ReviewStatsDto = {
1571
- type: 'object',
1572
- properties: {
1573
- total: {
1581
+ offset: {
1574
1582
  type: 'number',
1575
- description: 'Total pending reviews'
1583
+ description: 'Number of items skipped',
1584
+ example: 0
1576
1585
  },
1577
- byType: {
1578
- type: 'object',
1579
- description: 'Count by type'
1586
+ summary: {
1587
+ description:
1588
+ 'Amount summary for the full filtered set (#514). Present only when the request has a single account OR category viewpoint; omitted for search-only / plain-list / dual-perspective requests.',
1589
+ allOf: [
1590
+ {
1591
+ $ref: '#/components/schemas/TransactionListSummaryDto'
1592
+ }
1593
+ ]
1580
1594
  },
1581
- oldestPending: {
1582
- format: 'date-time',
1595
+ viewpoint: {
1596
+ description:
1597
+ 'Viewpoint metadata (ADR-0126). Present only for a single category filter (category + flow, no accountId); dual-perspective requests are viewpoint-less (raw signs, no viewpointAmount).',
1598
+ allOf: [
1599
+ {
1600
+ $ref: '#/components/schemas/TransactionListViewpointDto'
1601
+ }
1602
+ ]
1603
+ }
1604
+ },
1605
+ required: ['data', 'total', 'limit', 'offset']
1606
+ } as const;
1607
+
1608
+ export const $TagSuggestionDto = {
1609
+ type: 'object',
1610
+ properties: {
1611
+ tag: {
1583
1612
  type: 'string',
1584
- description: 'Oldest pending review date'
1613
+ description: 'Tag name',
1614
+ example: 'Monthly'
1615
+ },
1616
+ count: {
1617
+ type: 'number',
1618
+ description: 'Usage count across ACTIVE transactions',
1619
+ example: 12
1585
1620
  }
1586
1621
  },
1587
- required: ['total', 'byType']
1622
+ required: ['tag', 'count']
1588
1623
  } as const;
1589
1624
 
1590
- export const $DecisionOptionDto = {
1625
+ export const $TagSuggestionsResponseDto = {
1591
1626
  type: 'object',
1592
1627
  properties: {
1593
- value: {
1628
+ data: {
1629
+ description: 'Tag suggestions sorted as requested',
1630
+ type: 'array',
1631
+ items: {
1632
+ $ref: '#/components/schemas/TagSuggestionDto'
1633
+ }
1634
+ }
1635
+ },
1636
+ required: ['data']
1637
+ } as const;
1638
+
1639
+ export const $UpdateTransactionDto = {
1640
+ type: 'object',
1641
+ properties: {
1642
+ flag: {
1594
1643
  type: 'string',
1595
- description: 'The action value to submit (e.g., UPGRADE_REPLACE, ACCEPT)',
1644
+ description: 'Transaction flag (CLEARED, PENDING, etc.)',
1596
1645
  enum: [
1597
- 'UPGRADE_REPLACE',
1598
- 'LINK_KEEP_BOTH',
1599
- 'IGNORE_NEW',
1600
- 'CONFIRM_DIFFERENT',
1601
- 'ACCEPT',
1602
- 'REJECT',
1603
- 'ACCEPT_AND_LEARN',
1604
- 'CHOOSE_OTHER',
1605
- 'CANCEL',
1606
- 'FIX',
1607
- 'IGNORE'
1608
- ]
1646
+ 'CLEARED',
1647
+ 'PENDING',
1648
+ 'PADDING',
1649
+ 'SUMMARIZE',
1650
+ 'TRANSFER',
1651
+ 'CONVERSIONS'
1652
+ ],
1653
+ example: 'CLEARED'
1609
1654
  },
1610
- labelKey: {
1655
+ payee: {
1611
1656
  type: 'string',
1612
- description:
1613
- 'i18n message key for display label (e.g., review.payee.accept.label)'
1657
+ description: 'Payee name',
1658
+ maxLength: 500,
1659
+ example: 'Whole Foods Market'
1614
1660
  },
1615
- descriptionKey: {
1661
+ narration: {
1616
1662
  type: 'string',
1617
- description: 'i18n message key for description'
1663
+ description: 'Transaction narration/description',
1664
+ maxLength: 1000,
1665
+ example: 'Weekly grocery shopping'
1618
1666
  },
1619
- recommended: {
1620
- type: 'boolean',
1621
- description: 'Whether this is the recommended option'
1667
+ tags: {
1668
+ description: 'Transaction tags',
1669
+ maxItems: 50,
1670
+ example: ['groceries', 'weekly'],
1671
+ type: 'array',
1672
+ items: {
1673
+ type: 'string'
1674
+ }
1675
+ },
1676
+ links: {
1677
+ description: 'Transaction links',
1678
+ maxItems: 50,
1679
+ example: ['invoice-2024-001'],
1680
+ type: 'array',
1681
+ items: {
1682
+ type: 'string'
1683
+ }
1684
+ },
1685
+ meta: {
1686
+ type: 'object',
1687
+ description: 'Transaction metadata (JSON object)',
1688
+ example: {
1689
+ category: 'Food:Groceries',
1690
+ reviewed: true
1691
+ }
1692
+ }
1693
+ }
1694
+ } as const;
1695
+
1696
+ export const $BalanceResponseDto = {
1697
+ type: 'object',
1698
+ properties: {
1699
+ account: {
1700
+ type: 'string',
1701
+ description: 'Account name',
1702
+ example: 'Assets:Checking'
1703
+ },
1704
+ balance: {
1705
+ type: 'string',
1706
+ description: 'Balance amount (decimal string for precision)',
1707
+ example: '12345.67'
1708
+ },
1709
+ currency: {
1710
+ type: 'string',
1711
+ description: 'Currency code',
1712
+ example: 'USD'
1713
+ },
1714
+ date: {
1715
+ type: 'string',
1716
+ description: 'Date of the balance calculation (ISO 8601)',
1717
+ example: '2024-12-31T00:00:00.000Z'
1622
1718
  }
1623
1719
  },
1624
- required: ['value', 'labelKey']
1720
+ required: ['account', 'balance', 'currency', 'date']
1625
1721
  } as const;
1626
1722
 
1627
- export const $ReviewDetailDto = {
1723
+ export const $MultiCurrencyBalanceResponseDto = {
1724
+ type: 'object',
1725
+ properties: {
1726
+ account: {
1727
+ type: 'string',
1728
+ description: 'Account name',
1729
+ example: 'Assets:Checking'
1730
+ },
1731
+ balances: {
1732
+ type: 'object',
1733
+ description: 'Balances by currency',
1734
+ example: {
1735
+ USD: '12345.67',
1736
+ CNY: '100000.00'
1737
+ }
1738
+ },
1739
+ date: {
1740
+ type: 'string',
1741
+ description: 'Date of the balance calculation (ISO 8601)',
1742
+ example: '2024-12-31T00:00:00.000Z'
1743
+ }
1744
+ },
1745
+ required: ['account', 'balances', 'date']
1746
+ } as const;
1747
+
1748
+ export const $TransactionSummaryDto = {
1749
+ type: 'object',
1750
+ properties: {
1751
+ id: {
1752
+ type: 'string',
1753
+ description: 'Transaction ID (null if transaction deleted)',
1754
+ example: 'clh1234567890abcdef',
1755
+ nullable: true
1756
+ },
1757
+ date: {
1758
+ type: 'string',
1759
+ description: 'Transaction date (YYYY-MM-DD)',
1760
+ example: '2024-03-15'
1761
+ },
1762
+ amount: {
1763
+ type: 'string',
1764
+ description: 'Transaction amount (absolute value)',
1765
+ example: '128.50'
1766
+ },
1767
+ currency: {
1768
+ type: 'string',
1769
+ description: 'Currency code',
1770
+ example: 'CNY'
1771
+ },
1772
+ payee: {
1773
+ type: 'string',
1774
+ description: 'Payee/Merchant name',
1775
+ example: 'Starbucks'
1776
+ },
1777
+ narration: {
1778
+ type: 'string',
1779
+ description: 'Transaction narration',
1780
+ example: 'Coffee purchase'
1781
+ },
1782
+ accountName: {
1783
+ type: 'string',
1784
+ description: 'Source account name (first posting)',
1785
+ example: 'Assets:Checking'
1786
+ },
1787
+ sourceType: {
1788
+ type: 'string',
1789
+ description:
1790
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1791
+ },
1792
+ sourcePlatform: {
1793
+ type: 'string',
1794
+ description: 'Source platform (e.g., alipay, wechat)',
1795
+ example: 'alipay'
1796
+ }
1797
+ },
1798
+ required: ['date', 'amount', 'currency', 'narration']
1799
+ } as const;
1800
+
1801
+ export const $ReviewSummaryDto = {
1628
1802
  type: 'object',
1629
1803
  properties: {
1630
1804
  id: {
@@ -1722,22 +1896,6 @@ export const $ReviewDetailDto = {
1722
1896
  type: 'string',
1723
1897
  description:
1724
1898
  'Transaction date/time (convenience field for mobile display)'
1725
- },
1726
- reviewData: {
1727
- type: 'object',
1728
- description:
1729
- 'Review-type-specific data (JSONB). Structure varies by type: DUPLICATE: {newTransaction, existingTransaction, matchScore}, RULE_MATCH: {transaction, matchedRule, suggestedAccount}, PAYEE_MATCH: {originalPayee, suggestedPayee}, ACCOUNT_VALIDATION: {invalidAccount, suggestedCorrection, similarAccounts}, PIPELINE_ERROR: {errorType, errorMessage}'
1730
- },
1731
- decisionOptions: {
1732
- description: 'Available decision options',
1733
- type: 'array',
1734
- items: {
1735
- $ref: '#/components/schemas/DecisionOptionDto'
1736
- }
1737
- },
1738
- transactionId: {
1739
- type: 'string',
1740
- description: 'Related transaction ID if applicable'
1741
1899
  }
1742
1900
  },
1743
1901
  required: [
@@ -1749,25 +1907,240 @@ export const $ReviewDetailDto = {
1749
1907
  'summaryKey',
1750
1908
  'matchReasons',
1751
1909
  'sourceType',
1752
- 'createdAt',
1753
- 'reviewData',
1754
- 'decisionOptions'
1910
+ 'createdAt'
1755
1911
  ]
1756
1912
  } as const;
1757
1913
 
1758
- export const $ResolveReviewDto = {
1914
+ export const $ReviewListResponseDto = {
1759
1915
  type: 'object',
1760
1916
  properties: {
1761
- action: {
1762
- type: 'string',
1763
- description:
1764
- 'Decision action. Valid actions vary by review type — see DecisionOptionDto.value returned by the review detail endpoint.',
1765
- enum: [
1766
- 'UPGRADE_REPLACE',
1767
- 'LINK_KEEP_BOTH',
1768
- 'IGNORE_NEW',
1769
- 'CONFIRM_DIFFERENT',
1770
- 'ACCEPT',
1917
+ items: {
1918
+ type: 'array',
1919
+ items: {
1920
+ $ref: '#/components/schemas/ReviewSummaryDto'
1921
+ }
1922
+ },
1923
+ total: {
1924
+ type: 'number',
1925
+ description: 'Total number of items'
1926
+ },
1927
+ page: {
1928
+ type: 'number',
1929
+ description: 'Current page number'
1930
+ },
1931
+ limit: {
1932
+ type: 'number',
1933
+ description: 'Items per page'
1934
+ },
1935
+ hasMore: {
1936
+ type: 'boolean',
1937
+ description: 'Whether there are more pages'
1938
+ }
1939
+ },
1940
+ required: ['items', 'total', 'page', 'limit', 'hasMore']
1941
+ } as const;
1942
+
1943
+ export const $ReviewStatsDto = {
1944
+ type: 'object',
1945
+ properties: {
1946
+ total: {
1947
+ type: 'number',
1948
+ description: 'Total pending reviews'
1949
+ },
1950
+ byType: {
1951
+ type: 'object',
1952
+ description: 'Count by type'
1953
+ },
1954
+ oldestPending: {
1955
+ format: 'date-time',
1956
+ type: 'string',
1957
+ description: 'Oldest pending review date'
1958
+ }
1959
+ },
1960
+ required: ['total', 'byType']
1961
+ } as const;
1962
+
1963
+ export const $DecisionOptionDto = {
1964
+ type: 'object',
1965
+ properties: {
1966
+ value: {
1967
+ type: 'string',
1968
+ description: 'The action value to submit (e.g., UPGRADE_REPLACE, ACCEPT)',
1969
+ enum: [
1970
+ 'UPGRADE_REPLACE',
1971
+ 'LINK_KEEP_BOTH',
1972
+ 'IGNORE_NEW',
1973
+ 'CONFIRM_DIFFERENT',
1974
+ 'ACCEPT',
1975
+ 'REJECT',
1976
+ 'ACCEPT_AND_LEARN',
1977
+ 'CHOOSE_OTHER',
1978
+ 'CANCEL',
1979
+ 'FIX',
1980
+ 'IGNORE'
1981
+ ]
1982
+ },
1983
+ labelKey: {
1984
+ type: 'string',
1985
+ description:
1986
+ 'i18n message key for display label (e.g., review.payee.accept.label)'
1987
+ },
1988
+ descriptionKey: {
1989
+ type: 'string',
1990
+ description: 'i18n message key for description'
1991
+ },
1992
+ recommended: {
1993
+ type: 'boolean',
1994
+ description: 'Whether this is the recommended option'
1995
+ }
1996
+ },
1997
+ required: ['value', 'labelKey']
1998
+ } as const;
1999
+
2000
+ export const $ReviewDetailDto = {
2001
+ type: 'object',
2002
+ properties: {
2003
+ id: {
2004
+ type: 'string',
2005
+ description: 'Review item ID'
2006
+ },
2007
+ type: {
2008
+ type: 'string',
2009
+ description: 'Review type',
2010
+ enum: [
2011
+ 'DUPLICATE',
2012
+ 'RULE_MATCH',
2013
+ 'PAYEE_MATCH',
2014
+ 'ACCOUNT_VALIDATION',
2015
+ 'PIPELINE_ERROR'
2016
+ ]
2017
+ },
2018
+ status: {
2019
+ type: 'string',
2020
+ description: 'Review status',
2021
+ enum: ['PENDING', 'RESOLVED', 'EXPIRED', 'CANCELLED']
2022
+ },
2023
+ confidence: {
2024
+ type: 'number',
2025
+ description: 'Confidence score (0-1)'
2026
+ },
2027
+ confidenceLevel: {
2028
+ type: 'string',
2029
+ description:
2030
+ 'Confidence level derived from score. Null for error-type reviews (ACCOUNT_VALIDATION/PIPELINE_ERROR) which carry no confidence.',
2031
+ enum: ['HIGH', 'MEDIUM', 'LOW'],
2032
+ nullable: true
2033
+ },
2034
+ summaryKey: {
2035
+ type: 'string',
2036
+ description:
2037
+ 'i18n message key for summary (e.g., review.summary.duplicate). Translate on frontend with summaryParams.'
2038
+ },
2039
+ summaryParams: {
2040
+ type: 'object',
2041
+ description:
2042
+ 'Parameters for summary message interpolation (e.g., { date: "2024-01-15", amount: "50" })',
2043
+ additionalProperties: {
2044
+ type: 'string'
2045
+ }
2046
+ },
2047
+ matchReasons: {
2048
+ description: 'Human-readable reasons for branching',
2049
+ type: 'array',
2050
+ items: {
2051
+ type: 'string'
2052
+ }
2053
+ },
2054
+ sourceType: {
2055
+ type: 'string',
2056
+ description:
2057
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
2058
+ },
2059
+ sourcePlatform: {
2060
+ type: 'string',
2061
+ description: 'Source platform (e.g., alipay, wechat)'
2062
+ },
2063
+ createdAt: {
2064
+ format: 'date-time',
2065
+ type: 'string',
2066
+ description: 'Creation timestamp'
2067
+ },
2068
+ transaction: {
2069
+ description:
2070
+ 'Transaction summary for display (null if transaction deleted)',
2071
+ nullable: true,
2072
+ allOf: [
2073
+ {
2074
+ $ref: '#/components/schemas/TransactionSummaryDto'
2075
+ }
2076
+ ]
2077
+ },
2078
+ amount: {
2079
+ type: 'string',
2080
+ description: 'Transaction amount (convenience field for mobile display)'
2081
+ },
2082
+ currency: {
2083
+ type: 'string',
2084
+ description: 'Currency code (convenience field for mobile display)'
2085
+ },
2086
+ merchantName: {
2087
+ type: 'string',
2088
+ description: 'Payee/Merchant name (convenience field for mobile display)'
2089
+ },
2090
+ accountName: {
2091
+ type: 'string',
2092
+ description: 'Account name (convenience field for mobile display)'
2093
+ },
2094
+ transactionTime: {
2095
+ type: 'string',
2096
+ description:
2097
+ 'Transaction date/time (convenience field for mobile display)'
2098
+ },
2099
+ reviewData: {
2100
+ type: 'object',
2101
+ description:
2102
+ 'Review-type-specific data (JSONB). Structure varies by type: DUPLICATE: {newTransaction, existingTransaction, matchScore}, RULE_MATCH: {transaction, matchedRule, suggestedAccount}, PAYEE_MATCH: {originalPayee, suggestedPayee}, ACCOUNT_VALIDATION: {invalidAccount, suggestedCorrection, similarAccounts}, PIPELINE_ERROR: {errorType, errorMessage}'
2103
+ },
2104
+ decisionOptions: {
2105
+ description: 'Available decision options',
2106
+ type: 'array',
2107
+ items: {
2108
+ $ref: '#/components/schemas/DecisionOptionDto'
2109
+ }
2110
+ },
2111
+ transactionId: {
2112
+ type: 'string',
2113
+ description: 'Related transaction ID if applicable'
2114
+ }
2115
+ },
2116
+ required: [
2117
+ 'id',
2118
+ 'type',
2119
+ 'status',
2120
+ 'confidence',
2121
+ 'confidenceLevel',
2122
+ 'summaryKey',
2123
+ 'matchReasons',
2124
+ 'sourceType',
2125
+ 'createdAt',
2126
+ 'reviewData',
2127
+ 'decisionOptions'
2128
+ ]
2129
+ } as const;
2130
+
2131
+ export const $ResolveReviewDto = {
2132
+ type: 'object',
2133
+ properties: {
2134
+ action: {
2135
+ type: 'string',
2136
+ description:
2137
+ 'Decision action. Valid actions vary by review type — see DecisionOptionDto.value returned by the review detail endpoint.',
2138
+ enum: [
2139
+ 'UPGRADE_REPLACE',
2140
+ 'LINK_KEEP_BOTH',
2141
+ 'IGNORE_NEW',
2142
+ 'CONFIRM_DIFFERENT',
2143
+ 'ACCEPT',
1771
2144
  'REJECT',
1772
2145
  'ACCEPT_AND_LEARN',
1773
2146
  'CHOOSE_OTHER',
@@ -1811,7 +2184,8 @@ export const $ResolveResultDto = {
1811
2184
  },
1812
2185
  resolutionId: {
1813
2186
  type: 'string',
1814
- description: 'Resolution ID for undo'
2187
+ description:
2188
+ 'Resolution ID for undo. Absent when the resolver rejected the decision (review stayed PENDING).'
1815
2189
  },
1816
2190
  canUndo: {
1817
2191
  type: 'boolean',
@@ -1829,7 +2203,7 @@ export const $ResolveResultDto = {
1829
2203
  example: 'rule_01HXK5V8N2M3P4Q5R6S7T8U9V0'
1830
2204
  }
1831
2205
  },
1832
- required: ['success', 'resolutionId', 'canUndo', 'undoDeadline']
2206
+ required: ['success']
1833
2207
  } as const;
1834
2208
 
1835
2209
  export const $UndoResultDto = {
@@ -2700,26 +3074,183 @@ export const $UpdateCommodityDto = {
2700
3074
  }
2701
3075
  } as const;
2702
3076
 
2703
- export const $CreateRecurringRuleDto = {
3077
+ export const $CreateBeanPriceDto = {
2704
3078
  type: 'object',
2705
3079
  properties: {
2706
- name: {
3080
+ currency: {
2707
3081
  type: 'string',
2708
- description: 'Rule name (unique per user)',
2709
- maxLength: 100
3082
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3083
+ example: 'USD'
2710
3084
  },
2711
- icon: {
3085
+ quoteCurrency: {
2712
3086
  type: 'string',
2713
- description: 'Icon emoji',
2714
- maxLength: 10
3087
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3088
+ example: 'CNY'
2715
3089
  },
2716
- frequency: {
3090
+ amount: {
3091
+ type: 'number',
3092
+ description:
3093
+ 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
3094
+ example: 175.5,
3095
+ minimum: 0
3096
+ },
3097
+ date: {
2717
3098
  type: 'string',
2718
- description: 'Recurring frequency',
2719
- enum: [
2720
- 'WEEKLY',
2721
- 'BIWEEKLY',
2722
- 'MONTHLY',
3099
+ description: 'Price date (ISO 8601 format)',
3100
+ example: '2024-11-05'
3101
+ },
3102
+ metadata: {
3103
+ type: 'object',
3104
+ description:
3105
+ 'Metadata (validated by Zod schema, max field lengths enforced)',
3106
+ example: {
3107
+ source: 'MANUAL',
3108
+ note: 'Bank valuation report',
3109
+ confidence: 0.95
3110
+ }
3111
+ }
3112
+ },
3113
+ required: ['currency', 'quoteCurrency', 'amount', 'date']
3114
+ } as const;
3115
+
3116
+ export const $PriceResponseDto = {
3117
+ type: 'object',
3118
+ properties: {
3119
+ id: {
3120
+ type: 'string',
3121
+ description: 'Unique identifier',
3122
+ example: 'uuid-123-456'
3123
+ },
3124
+ userId: {
3125
+ type: 'string',
3126
+ description: 'User ID (owner of the price)',
3127
+ example: 'user-123'
3128
+ },
3129
+ currency: {
3130
+ type: 'string',
3131
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3132
+ example: 'BTC'
3133
+ },
3134
+ quoteCurrency: {
3135
+ type: 'string',
3136
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
3137
+ example: 'USD'
3138
+ },
3139
+ amount: {
3140
+ type: 'number',
3141
+ description:
3142
+ 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
3143
+ example: 50000
3144
+ },
3145
+ date: {
3146
+ type: 'string',
3147
+ description:
3148
+ 'Price date (ISO 8601 format). Represents the date this price was valid.',
3149
+ example: '2024-01-01',
3150
+ format: 'date'
3151
+ },
3152
+ meta: {
3153
+ type: 'object',
3154
+ description:
3155
+ 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
3156
+ example: {
3157
+ source: 'MANUAL',
3158
+ note: 'User-defined price',
3159
+ confidence: 1
3160
+ }
3161
+ },
3162
+ createdAt: {
3163
+ format: 'date-time',
3164
+ type: 'string',
3165
+ description: 'Creation timestamp',
3166
+ example: '2024-11-03T10:00:00Z'
3167
+ },
3168
+ updatedAt: {
3169
+ format: 'date-time',
3170
+ type: 'string',
3171
+ description: 'Last update timestamp',
3172
+ example: '2024-11-03T10:00:00Z'
3173
+ }
3174
+ },
3175
+ required: [
3176
+ 'id',
3177
+ 'userId',
3178
+ 'currency',
3179
+ 'quoteCurrency',
3180
+ 'amount',
3181
+ 'date',
3182
+ 'meta',
3183
+ 'createdAt',
3184
+ 'updatedAt'
3185
+ ]
3186
+ } as const;
3187
+
3188
+ export const $PriceListResponseDto = {
3189
+ type: 'object',
3190
+ properties: {
3191
+ items: {
3192
+ description: 'List of prices',
3193
+ type: 'array',
3194
+ items: {
3195
+ $ref: '#/components/schemas/PriceResponseDto'
3196
+ }
3197
+ },
3198
+ total: {
3199
+ type: 'number',
3200
+ description: 'Total number of prices',
3201
+ example: 42
3202
+ }
3203
+ },
3204
+ required: ['items', 'total']
3205
+ } as const;
3206
+
3207
+ export const $UpdateBeanPriceDto = {
3208
+ type: 'object',
3209
+ properties: {
3210
+ currency: {
3211
+ type: 'string',
3212
+ description: 'Currency being priced'
3213
+ },
3214
+ quoteCurrency: {
3215
+ type: 'string',
3216
+ description: 'Quote currency (pricing currency)'
3217
+ },
3218
+ amount: {
3219
+ type: 'number',
3220
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
3221
+ minimum: 0
3222
+ },
3223
+ date: {
3224
+ type: 'string',
3225
+ description: 'Price date (ISO 8601 format)'
3226
+ },
3227
+ metadata: {
3228
+ type: 'object',
3229
+ description: 'Metadata'
3230
+ }
3231
+ }
3232
+ } as const;
3233
+
3234
+ export const $CreateRecurringRuleDto = {
3235
+ type: 'object',
3236
+ properties: {
3237
+ name: {
3238
+ type: 'string',
3239
+ description: 'Rule name (unique per user)',
3240
+ maxLength: 100
3241
+ },
3242
+ icon: {
3243
+ type: 'string',
3244
+ description: 'Icon emoji',
3245
+ maxLength: 10
3246
+ },
3247
+ frequency: {
3248
+ type: 'string',
3249
+ description: 'Recurring frequency',
3250
+ enum: [
3251
+ 'WEEKLY',
3252
+ 'BIWEEKLY',
3253
+ 'MONTHLY',
2723
3254
  'BIMONTHLY',
2724
3255
  'QUARTERLY',
2725
3256
  'YEARLY',
@@ -3269,206 +3800,697 @@ export const $ExpectedTransactionResponseDto = {
3269
3800
  updatedAt: {
3270
3801
  format: 'date-time',
3271
3802
  type: 'string',
3272
- description: 'Updated at timestamp'
3803
+ description: 'Updated at timestamp'
3804
+ }
3805
+ },
3806
+ required: [
3807
+ 'id',
3808
+ 'userId',
3809
+ 'ruleId',
3810
+ 'expectedDate',
3811
+ 'expectedAmount',
3812
+ 'status',
3813
+ 'isOverdue',
3814
+ 'rule',
3815
+ 'createdAt',
3816
+ 'updatedAt'
3817
+ ]
3818
+ } as const;
3819
+
3820
+ export const $ExpectedTransactionListResponseDto = {
3821
+ type: 'object',
3822
+ properties: {
3823
+ items: {
3824
+ type: 'array',
3825
+ items: {
3826
+ $ref: '#/components/schemas/ExpectedTransactionResponseDto'
3827
+ }
3828
+ },
3829
+ total: {
3830
+ type: 'number',
3831
+ description: 'Total count'
3832
+ }
3833
+ },
3834
+ required: ['items', 'total']
3835
+ } as const;
3836
+
3837
+ export const $ConfirmMatchDto = {
3838
+ type: 'object',
3839
+ properties: {
3840
+ transactionId: {
3841
+ type: 'string',
3842
+ description: 'Transaction ID to match with'
3843
+ }
3844
+ },
3845
+ required: ['transactionId']
3846
+ } as const;
3847
+
3848
+ export const $EnterNowDto = {
3849
+ type: 'object',
3850
+ properties: {
3851
+ expenseAccount: {
3852
+ type: 'string',
3853
+ description:
3854
+ 'Override expense account (uses rule default if not provided)',
3855
+ maxLength: 200
3856
+ },
3857
+ paymentAccount: {
3858
+ type: 'string',
3859
+ description:
3860
+ 'Override payment account (uses rule default if not provided)',
3861
+ maxLength: 200
3862
+ },
3863
+ amount: {
3864
+ type: 'number',
3865
+ description: 'Override amount (uses expected amount if not provided)',
3866
+ minimum: 0
3867
+ },
3868
+ payee: {
3869
+ type: 'string',
3870
+ description: 'Override payee (uses rule default if not provided)',
3871
+ maxLength: 200
3872
+ },
3873
+ narration: {
3874
+ type: 'string',
3875
+ description: 'Optional narration',
3876
+ maxLength: 500
3877
+ }
3878
+ }
3879
+ } as const;
3880
+
3881
+ export const $ForecastItemDto = {
3882
+ type: 'object',
3883
+ properties: {
3884
+ rule: {
3885
+ type: 'string',
3886
+ description: 'Rule name',
3887
+ example: 'Rent'
3888
+ },
3889
+ ruleId: {
3890
+ type: 'string',
3891
+ description: 'Rule ID',
3892
+ example: 'clx123...'
3893
+ },
3894
+ amount: {
3895
+ type: 'number',
3896
+ description: 'Expected amount',
3897
+ example: 3000
3898
+ },
3899
+ date: {
3900
+ type: 'string',
3901
+ description: 'Expected date (YYYY-MM-DD)',
3902
+ example: '2024-04-01'
3903
+ },
3904
+ icon: {
3905
+ type: 'string',
3906
+ description: 'Rule icon emoji',
3907
+ example: '🏠',
3908
+ nullable: true
3909
+ },
3910
+ currency: {
3911
+ type: 'string',
3912
+ description: 'Currency code',
3913
+ example: 'CNY'
3914
+ }
3915
+ },
3916
+ required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
3917
+ } as const;
3918
+
3919
+ export const $MonthlyForecastDto = {
3920
+ type: 'object',
3921
+ properties: {
3922
+ month: {
3923
+ type: 'string',
3924
+ description: 'Month (YYYY-MM)',
3925
+ example: '2024-04'
3926
+ },
3927
+ expectedOutflow: {
3928
+ type: 'number',
3929
+ description: 'Total expected outflow for the month',
3930
+ example: 8500
3931
+ },
3932
+ itemCount: {
3933
+ type: 'number',
3934
+ description: 'Number of expected transactions',
3935
+ example: 3
3936
+ },
3937
+ byCurrency: {
3938
+ type: 'object',
3939
+ description: 'Breakdown by currency',
3940
+ example: {
3941
+ CNY: 8500,
3942
+ USD: 100
3943
+ }
3944
+ },
3945
+ items: {
3946
+ description: 'Individual forecast items',
3947
+ type: 'array',
3948
+ items: {
3949
+ $ref: '#/components/schemas/ForecastItemDto'
3950
+ }
3951
+ }
3952
+ },
3953
+ required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
3954
+ } as const;
3955
+
3956
+ export const $ForecastResponseDto = {
3957
+ type: 'object',
3958
+ properties: {
3959
+ forecast: {
3960
+ description: 'Monthly forecast data',
3961
+ type: 'array',
3962
+ items: {
3963
+ $ref: '#/components/schemas/MonthlyForecastDto'
3964
+ }
3965
+ },
3966
+ totalOutflow: {
3967
+ type: 'number',
3968
+ description: 'Total expected outflow across all months',
3969
+ example: 25500
3970
+ },
3971
+ totalByCurrency: {
3972
+ type: 'object',
3973
+ description: 'Total by currency across all months',
3974
+ example: {
3975
+ CNY: 25500,
3976
+ USD: 300
3977
+ }
3978
+ },
3979
+ rulesCount: {
3980
+ type: 'number',
3981
+ description: 'Number of active recurring rules included',
3982
+ example: 5
3983
+ },
3984
+ periodStart: {
3985
+ type: 'string',
3986
+ description: 'Forecast period start date',
3987
+ example: '2024-04-01'
3988
+ },
3989
+ periodEnd: {
3990
+ type: 'string',
3991
+ description: 'Forecast period end date',
3992
+ example: '2024-06-30'
3993
+ }
3994
+ },
3995
+ required: [
3996
+ 'forecast',
3997
+ 'totalOutflow',
3998
+ 'totalByCurrency',
3999
+ 'rulesCount',
4000
+ 'periodStart',
4001
+ 'periodEnd'
4002
+ ]
4003
+ } as const;
4004
+
4005
+ export const $CurrencyBalanceDto = {
4006
+ type: 'object',
4007
+ properties: {
4008
+ currency: {
4009
+ type: 'string',
4010
+ description: 'ISO 4217 currency code',
4011
+ example: 'CNY'
4012
+ },
4013
+ balance: {
4014
+ type: 'string',
4015
+ description: 'Balance amount',
4016
+ example: '500000.00'
4017
+ }
4018
+ },
4019
+ required: ['currency', 'balance']
4020
+ } as const;
4021
+
4022
+ export const $TimeSeriesPointDto = {
4023
+ type: 'object',
4024
+ properties: {
4025
+ date: {
4026
+ type: 'string',
4027
+ description: 'Date in YYYY-MM-DD format',
4028
+ example: '2024-06-15'
4029
+ },
4030
+ value: {
4031
+ type: 'string',
4032
+ description: 'Value at this date (in base currency)',
4033
+ example: '500000.00'
4034
+ },
4035
+ change: {
4036
+ type: 'object',
4037
+ description: 'Change from previous point',
4038
+ example: '5000.00'
4039
+ },
4040
+ assets: {
4041
+ type: 'string',
4042
+ description: 'Total assets at this date (in base currency)',
4043
+ example: '494338.00'
4044
+ },
4045
+ liabilities: {
4046
+ type: 'string',
4047
+ description: 'Total liabilities at this date (in base currency)',
4048
+ example: '310098.00'
4049
+ },
4050
+ byCurrency: {
4051
+ description: 'Multi-currency breakdown for this point',
4052
+ type: 'array',
4053
+ items: {
4054
+ $ref: '#/components/schemas/CurrencyBalanceDto'
4055
+ }
4056
+ }
4057
+ },
4058
+ required: ['date', 'value']
4059
+ } as const;
4060
+
4061
+ export const $TrendSummaryDto = {
4062
+ type: 'object',
4063
+ properties: {
4064
+ startValue: {
4065
+ type: 'string',
4066
+ description: 'Value at start of period',
4067
+ example: '450000.00'
4068
+ },
4069
+ endValue: {
4070
+ type: 'string',
4071
+ description: 'Value at end of period',
4072
+ example: '500000.00'
4073
+ },
4074
+ totalChange: {
4075
+ type: 'string',
4076
+ description: 'Total change over period',
4077
+ example: '50000.00'
4078
+ },
4079
+ totalChangePercentage: {
4080
+ type: 'string',
4081
+ description: 'Total change percentage',
4082
+ example: '+11.11%'
4083
+ }
4084
+ },
4085
+ required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
4086
+ } as const;
4087
+
4088
+ export const $MultiCurrencyPointDto = {
4089
+ type: 'object',
4090
+ properties: {
4091
+ date: {
4092
+ type: 'string',
4093
+ description: 'Date in YYYY-MM-DD format',
4094
+ example: '2024-06-15'
4095
+ },
4096
+ byCurrency: {
4097
+ description: 'Balances by currency',
4098
+ type: 'array',
4099
+ items: {
4100
+ $ref: '#/components/schemas/CurrencyBalanceDto'
4101
+ }
4102
+ }
4103
+ },
4104
+ required: ['date', 'byCurrency']
4105
+ } as const;
4106
+
4107
+ export const $PortfolioTrendsResponseDto = {
4108
+ type: 'object',
4109
+ properties: {
4110
+ series: {
4111
+ description: 'Time series data points',
4112
+ type: 'array',
4113
+ items: {
4114
+ $ref: '#/components/schemas/TimeSeriesPointDto'
4115
+ }
4116
+ },
4117
+ summary: {
4118
+ description: 'Period summary',
4119
+ allOf: [
4120
+ {
4121
+ $ref: '#/components/schemas/TrendSummaryDto'
4122
+ }
4123
+ ]
4124
+ },
4125
+ period: {
4126
+ type: 'string',
4127
+ description: 'Period requested',
4128
+ example: '6m'
4129
+ },
4130
+ granularity: {
4131
+ type: 'string',
4132
+ description: 'Data granularity',
4133
+ example: 'month'
4134
+ },
4135
+ currency: {
4136
+ type: 'string',
4137
+ description: 'Base currency for converted values',
4138
+ example: 'CNY'
4139
+ },
4140
+ byCurrency: {
4141
+ description:
4142
+ 'Multi-currency time series (each point has currency breakdown)',
4143
+ type: 'array',
4144
+ items: {
4145
+ $ref: '#/components/schemas/MultiCurrencyPointDto'
4146
+ }
4147
+ },
4148
+ warnings: {
4149
+ description: 'Exchange rate warnings',
4150
+ type: 'array',
4151
+ items: {
4152
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
4153
+ }
4154
+ }
4155
+ },
4156
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
4157
+ } as const;
4158
+
4159
+ export const $CashFlowPointDto = {
4160
+ type: 'object',
4161
+ properties: {
4162
+ month: {
4163
+ type: 'string',
4164
+ description: 'Month key (YYYY-MM)',
4165
+ example: '2024-03'
4166
+ },
4167
+ income: {
4168
+ type: 'string',
4169
+ description: 'Income in base currency (absolute, converted)',
4170
+ example: '10000.00'
4171
+ },
4172
+ expense: {
4173
+ type: 'string',
4174
+ description: 'Expense in base currency (absolute, converted)',
4175
+ example: '5000.00'
4176
+ },
4177
+ netSavings: {
4178
+ type: 'string',
4179
+ description: 'netSavings = income − expense (savings positive)',
4180
+ example: '5000.00'
4181
+ }
4182
+ },
4183
+ required: ['month', 'income', 'expense', 'netSavings']
4184
+ } as const;
4185
+
4186
+ export const $CashFlowTrendSummaryDto = {
4187
+ type: 'object',
4188
+ properties: {
4189
+ totalIncome: {
4190
+ type: 'string',
4191
+ description: 'Total income across the period',
4192
+ example: '60000.00'
4193
+ },
4194
+ totalExpense: {
4195
+ type: 'string',
4196
+ description: 'Total expense across the period',
4197
+ example: '30000.00'
4198
+ },
4199
+ totalNetSavings: {
4200
+ type: 'string',
4201
+ description: 'income − expense across the period',
4202
+ example: '30000.00'
4203
+ },
4204
+ averageMonthlyNetSavings: {
4205
+ type: 'string',
4206
+ description:
4207
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
4208
+ example: '5000.00'
4209
+ }
4210
+ },
4211
+ required: [
4212
+ 'totalIncome',
4213
+ 'totalExpense',
4214
+ 'totalNetSavings',
4215
+ 'averageMonthlyNetSavings'
4216
+ ]
4217
+ } as const;
4218
+
4219
+ export const $CashFlowTrendsResponseDto = {
4220
+ type: 'object',
4221
+ properties: {
4222
+ series: {
4223
+ description:
4224
+ 'Monthly cash-flow series (fixed N-month window, zero-filled)',
4225
+ type: 'array',
4226
+ items: {
4227
+ $ref: '#/components/schemas/CashFlowPointDto'
4228
+ }
4229
+ },
4230
+ summary: {
4231
+ description: 'Period totals',
4232
+ allOf: [
4233
+ {
4234
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
4235
+ }
4236
+ ]
4237
+ },
4238
+ period: {
4239
+ type: 'string',
4240
+ description: 'Period requested',
4241
+ example: '6m'
4242
+ },
4243
+ granularity: {
4244
+ type: 'string',
4245
+ description: 'Data granularity (v1 returns month buckets)',
4246
+ example: 'month'
4247
+ },
4248
+ currency: {
4249
+ type: 'string',
4250
+ description: 'Base currency for converted values',
4251
+ example: 'CNY'
4252
+ },
4253
+ warnings: {
4254
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
4255
+ type: 'array',
4256
+ items: {
4257
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
4258
+ }
4259
+ }
4260
+ },
4261
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
4262
+ } as const;
4263
+
4264
+ export const $GenerateSnapshotBody = {
4265
+ type: 'object',
4266
+ properties: {}
4267
+ } as const;
4268
+
4269
+ export const $GenerateSnapshotResponse = {
4270
+ type: 'object',
4271
+ properties: {}
4272
+ } as const;
4273
+
4274
+ export const $BackfillSnapshotsBody = {
4275
+ type: 'object',
4276
+ properties: {}
4277
+ } as const;
4278
+
4279
+ export const $BackfillSnapshotsResponse = {
4280
+ type: 'object',
4281
+ properties: {}
4282
+ } as const;
4283
+
4284
+ export const $DeleteOwnUserDto = {
4285
+ type: 'object',
4286
+ properties: {
4287
+ accessToken: {
4288
+ type: 'string',
4289
+ description: 'Access token for user verification',
4290
+ example: 'abc123xyz'
4291
+ }
4292
+ },
4293
+ required: ['accessToken']
4294
+ } as const;
4295
+
4296
+ export const $UserSettingsResponseDto = {
4297
+ type: 'object',
4298
+ properties: {
4299
+ baseCurrency: {
4300
+ type: 'string',
4301
+ description:
4302
+ 'Base currency (ISO 4217) for net-worth/report aggregation. Independent of region (ADR-0006).',
4303
+ example: 'USD',
4304
+ nullable: true
3273
4305
  }
3274
4306
  },
3275
- required: [
3276
- 'id',
3277
- 'userId',
3278
- 'ruleId',
3279
- 'expectedDate',
3280
- 'expectedAmount',
3281
- 'status',
3282
- 'isOverdue',
3283
- 'rule',
3284
- 'createdAt',
3285
- 'updatedAt'
3286
- ]
4307
+ required: ['baseCurrency']
3287
4308
  } as const;
3288
4309
 
3289
- export const $ExpectedTransactionListResponseDto = {
4310
+ export const $UserResponseDto = {
3290
4311
  type: 'object',
3291
4312
  properties: {
3292
- items: {
4313
+ id: {
4314
+ type: 'string',
4315
+ description: 'User ID'
4316
+ },
4317
+ role: {
4318
+ type: 'string',
4319
+ description: 'Assigned user role'
4320
+ },
4321
+ permissions: {
4322
+ description: 'Permission strings',
3293
4323
  type: 'array',
3294
4324
  items: {
3295
- $ref: '#/components/schemas/ExpectedTransactionResponseDto'
4325
+ type: 'string'
3296
4326
  }
3297
4327
  },
3298
- total: {
3299
- type: 'number',
3300
- description: 'Total count'
4328
+ settings: {
4329
+ description: 'User settings',
4330
+ allOf: [
4331
+ {
4332
+ $ref: '#/components/schemas/UserSettingsResponseDto'
4333
+ }
4334
+ ]
3301
4335
  }
3302
4336
  },
3303
- required: ['items', 'total']
4337
+ required: ['id', 'role', 'permissions', 'settings']
3304
4338
  } as const;
3305
4339
 
3306
- export const $ConfirmMatchDto = {
4340
+ export const $SignupDto = {
3307
4341
  type: 'object',
3308
4342
  properties: {
3309
- transactionId: {
4343
+ turnstileToken: {
3310
4344
  type: 'string',
3311
- description: 'Transaction ID to match with'
4345
+ description:
4346
+ 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4347
+ example: '0.abc123def456...'
3312
4348
  }
3313
- },
3314
- required: ['transactionId']
4349
+ }
3315
4350
  } as const;
3316
4351
 
3317
- export const $EnterNowDto = {
4352
+ export const $SignupResponseDto = {
3318
4353
  type: 'object',
3319
4354
  properties: {
3320
- expenseAccount: {
3321
- type: 'string',
3322
- description:
3323
- 'Override expense account (uses rule default if not provided)',
3324
- maxLength: 200
3325
- },
3326
- paymentAccount: {
4355
+ authToken: {
3327
4356
  type: 'string',
3328
- description:
3329
- 'Override payment account (uses rule default if not provided)',
3330
- maxLength: 200
3331
- },
3332
- amount: {
3333
- type: 'number',
3334
- description: 'Override amount (uses expected amount if not provided)',
3335
- minimum: 0
4357
+ description: 'JWT auth token',
4358
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
3336
4359
  },
3337
- payee: {
4360
+ accessToken: {
3338
4361
  type: 'string',
3339
- description: 'Override payee (uses rule default if not provided)',
3340
- maxLength: 200
4362
+ description: 'Auto-generated access token'
3341
4363
  },
3342
- narration: {
4364
+ role: {
3343
4365
  type: 'string',
3344
- description: 'Optional narration',
3345
- maxLength: 500
4366
+ description: 'Assigned user role',
4367
+ enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
3346
4368
  }
3347
- }
4369
+ },
4370
+ required: ['authToken', 'accessToken', 'role']
3348
4371
  } as const;
3349
4372
 
3350
- export const $ForecastItemDto = {
4373
+ export const $UpdateUserSettingDto = {
3351
4374
  type: 'object',
3352
4375
  properties: {
3353
- rule: {
3354
- type: 'string',
3355
- description: 'Rule name',
3356
- example: 'Rent'
3357
- },
3358
- ruleId: {
3359
- type: 'string',
3360
- description: 'Rule ID',
3361
- example: 'clx123...'
4376
+ secId: {
4377
+ type: 'number',
4378
+ description: 'Security ID'
3362
4379
  },
3363
- amount: {
4380
+ annualInterestRate: {
3364
4381
  type: 'number',
3365
- description: 'Expected amount',
3366
- example: 3000
4382
+ description: 'Annual interest rate',
4383
+ example: 0.05
3367
4384
  },
3368
- date: {
4385
+ currency: {
3369
4386
  type: 'string',
3370
- description: 'Expected date (YYYY-MM-DD)',
3371
- example: '2024-04-01'
4387
+ description: 'Currency code',
4388
+ example: 'USD'
3372
4389
  },
3373
- icon: {
4390
+ baseCurrency: {
3374
4391
  type: 'string',
3375
- description: 'Rule icon emoji',
3376
- example: '🏠',
3377
- nullable: true
4392
+ description: 'Base currency code',
4393
+ example: 'USD'
3378
4394
  },
3379
- currency: {
4395
+ benchmark: {
3380
4396
  type: 'string',
3381
- description: 'Currency code',
3382
- example: 'CNY'
3383
- }
3384
- },
3385
- required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
3386
- } as const;
3387
-
3388
- export const $MonthlyForecastDto = {
3389
- type: 'object',
3390
- properties: {
3391
- month: {
4397
+ description: 'Benchmark symbol',
4398
+ example: 'SPY'
4399
+ },
4400
+ colorScheme: {
3392
4401
  type: 'string',
3393
- description: 'Month (YYYY-MM)',
3394
- example: '2024-04'
4402
+ description: 'Color scheme',
4403
+ enum: ['DARK', 'LIGHT']
3395
4404
  },
3396
- expectedOutflow: {
3397
- type: 'number',
3398
- description: 'Total expected outflow for the month',
3399
- example: 8500
4405
+ dateRange: {
4406
+ type: 'string',
4407
+ description: 'Date range filter',
4408
+ example: '1y'
3400
4409
  },
3401
- itemCount: {
4410
+ emergencyFund: {
3402
4411
  type: 'number',
3403
- description: 'Number of expected transactions',
3404
- example: 3
4412
+ description: 'Emergency fund amount',
4413
+ example: 10000
3405
4414
  },
3406
- byCurrency: {
3407
- type: 'object',
3408
- description: 'Breakdown by currency',
3409
- example: {
3410
- CNY: 8500,
3411
- USD: 100
4415
+ 'filters.accounts': {
4416
+ description: 'Account filter IDs',
4417
+ type: 'array',
4418
+ items: {
4419
+ type: 'string'
3412
4420
  }
3413
4421
  },
3414
- items: {
3415
- description: 'Individual forecast items',
4422
+ 'filters.assetClasses': {
4423
+ description: 'Asset class filters',
3416
4424
  type: 'array',
3417
4425
  items: {
3418
- $ref: '#/components/schemas/ForecastItemDto'
4426
+ type: 'string'
3419
4427
  }
3420
- }
3421
- },
3422
- required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
3423
- } as const;
3424
-
3425
- export const $ForecastResponseDto = {
3426
- type: 'object',
3427
- properties: {
3428
- forecast: {
3429
- description: 'Monthly forecast data',
4428
+ },
4429
+ 'filters.dataSource': {
4430
+ type: 'string',
4431
+ description: 'Data source filter'
4432
+ },
4433
+ 'filters.symbol': {
4434
+ type: 'string',
4435
+ description: 'Symbol filter'
4436
+ },
4437
+ 'filters.tags': {
4438
+ description: 'Tag filters',
3430
4439
  type: 'array',
3431
4440
  items: {
3432
- $ref: '#/components/schemas/MonthlyForecastDto'
4441
+ type: 'string'
3433
4442
  }
3434
4443
  },
3435
- totalOutflow: {
3436
- type: 'number',
3437
- description: 'Total expected outflow across all months',
3438
- example: 25500
4444
+ isExperimentalFeatures: {
4445
+ type: 'boolean',
4446
+ description: 'Enable experimental features'
3439
4447
  },
3440
- totalByCurrency: {
3441
- type: 'object',
3442
- description: 'Total by currency across all months',
3443
- example: {
3444
- CNY: 25500,
3445
- USD: 300
3446
- }
4448
+ isRestrictedView: {
4449
+ type: 'boolean',
4450
+ description: 'Enable restricted view mode'
3447
4451
  },
3448
- rulesCount: {
4452
+ language: {
4453
+ type: 'string',
4454
+ description: 'Language code',
4455
+ example: 'en'
4456
+ },
4457
+ locale: {
4458
+ type: 'string',
4459
+ description: 'Locale code',
4460
+ example: 'en-US'
4461
+ },
4462
+ projectedTotalAmount: {
3449
4463
  type: 'number',
3450
- description: 'Number of active recurring rules included',
3451
- example: 5
4464
+ description: 'Projected total amount',
4465
+ example: 1000000
3452
4466
  },
3453
- periodStart: {
4467
+ retirementDate: {
3454
4468
  type: 'string',
3455
- description: 'Forecast period start date',
3456
- example: '2024-04-01'
4469
+ description: 'Retirement date in ISO 8601 format',
4470
+ example: '2050-01-01'
3457
4471
  },
3458
- periodEnd: {
4472
+ savingsRate: {
4473
+ type: 'number',
4474
+ description: 'Savings rate percentage',
4475
+ example: 0.2
4476
+ },
4477
+ viewMode: {
3459
4478
  type: 'string',
3460
- description: 'Forecast period end date',
3461
- example: '2024-06-30'
4479
+ description: 'View mode',
4480
+ enum: ['DEFAULT', 'ZEN']
3462
4481
  }
3463
- },
3464
- required: [
3465
- 'forecast',
3466
- 'totalOutflow',
3467
- 'totalByCurrency',
3468
- 'rulesCount',
3469
- 'periodStart',
3470
- 'periodEnd'
3471
- ]
4482
+ }
4483
+ } as const;
4484
+
4485
+ export const $UpdatePropertyDto = {
4486
+ type: 'object',
4487
+ properties: {
4488
+ value: {
4489
+ type: 'string',
4490
+ description: 'Property value'
4491
+ }
4492
+ },
4493
+ required: ['value']
3472
4494
  } as const;
3473
4495
 
3474
4496
  export const $CreateTransactionRuleDto = {
@@ -4120,151 +5142,456 @@ export const $TestRuleResponseDto = {
4120
5142
  required: ['ruleId', 'matches', 'confidence', 'matchDetails']
4121
5143
  } as const;
4122
5144
 
4123
- export const $DeleteOwnUserDto = {
5145
+ export const $CategoryCatalogEntryDto = {
4124
5146
  type: 'object',
4125
5147
  properties: {
4126
- accessToken: {
5148
+ slug: {
4127
5149
  type: 'string',
4128
- description: 'Access token for user verification',
4129
- example: 'abc123xyz'
5150
+ description: 'Category slug (single source-of-truth)',
5151
+ example: 'food'
5152
+ },
5153
+ scenario: {
5154
+ type: 'string',
5155
+ description: 'Display scenario group (maps to frontend picker _scenario)',
5156
+ enum: [
5157
+ 'expense',
5158
+ 'income',
5159
+ 'investment',
5160
+ 'banking',
5161
+ 'transfer',
5162
+ 'payment'
5163
+ ],
5164
+ example: 'expense'
5165
+ },
5166
+ icon: {
5167
+ type: 'string',
5168
+ description: 'Lucide icon name',
5169
+ example: 'utensils'
5170
+ },
5171
+ regions: {
5172
+ description: "Applicable regions ('*' = all, 'cn' = CN-only)",
5173
+ example: ['*'],
5174
+ type: 'array',
5175
+ items: {
5176
+ type: 'string'
5177
+ }
4130
5178
  }
4131
5179
  },
4132
- required: ['accessToken']
5180
+ required: ['slug', 'scenario', 'icon', 'regions']
4133
5181
  } as const;
4134
5182
 
4135
- export const $SignupDto = {
5183
+ export const $CategoryCatalogListResponseDto = {
4136
5184
  type: 'object',
4137
5185
  properties: {
4138
- turnstileToken: {
4139
- type: 'string',
5186
+ items: {
5187
+ description: 'Category entries (region-scoped, query-filtered)',
5188
+ type: 'array',
5189
+ items: {
5190
+ $ref: '#/components/schemas/CategoryCatalogEntryDto'
5191
+ }
5192
+ },
5193
+ total: {
5194
+ type: 'number',
4140
5195
  description:
4141
- 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4142
- example: '0.abc123def456...'
5196
+ 'Total category entries for the region (before query filtering)',
5197
+ example: 30
5198
+ },
5199
+ region: {
5200
+ type: 'string',
5201
+ description: 'Region code',
5202
+ example: 'cn'
4143
5203
  }
4144
- }
5204
+ },
5205
+ required: ['items', 'total', 'region']
4145
5206
  } as const;
4146
5207
 
4147
- export const $UpdateUserSettingDto = {
5208
+ export const $CreateBeanEventDto = {
4148
5209
  type: 'object',
4149
5210
  properties: {
4150
- secId: {
4151
- type: 'number',
4152
- description: 'Security ID'
5211
+ date: {
5212
+ type: 'string',
5213
+ description: 'Life event date (ISO 8601)',
5214
+ example: '2024-03-15'
4153
5215
  },
4154
- annualInterestRate: {
4155
- type: 'number',
4156
- description: 'Annual interest rate',
4157
- example: 0.05
5216
+ type: {
5217
+ type: 'string',
5218
+ description:
5219
+ 'Life event type (e.g., "employer", "location", "marital-status") — user-defined, no enum constraint at engine layer',
5220
+ example: 'employer'
4158
5221
  },
4159
- currency: {
5222
+ description: {
4160
5223
  type: 'string',
4161
- description: 'Currency code',
4162
- example: 'USD'
5224
+ description:
5225
+ 'Life event description. Empty string is a VALID value (distinct from absence).',
5226
+ example: 'Acme Corp'
4163
5227
  },
4164
- baseCurrency: {
5228
+ meta: {
5229
+ type: 'object',
5230
+ description:
5231
+ 'Product-side metadata (lives in BeanEvent.meta JSON, never in engine Event fields)',
5232
+ example: {
5233
+ note: 'Promotion'
5234
+ }
5235
+ }
5236
+ },
5237
+ required: ['date', 'type', 'description']
5238
+ } as const;
5239
+
5240
+ export const $EventResponseDto = {
5241
+ type: 'object',
5242
+ properties: {
5243
+ id: {
4165
5244
  type: 'string',
4166
- description: 'Base currency code',
4167
- example: 'USD'
5245
+ description: 'Unique identifier',
5246
+ example: 'uuid-123-456'
4168
5247
  },
4169
- benchmark: {
5248
+ userId: {
4170
5249
  type: 'string',
4171
- description: 'Benchmark symbol',
4172
- example: 'SPY'
5250
+ description: 'User ID (owner of the life event)',
5251
+ example: 'user-123'
4173
5252
  },
4174
- colorScheme: {
5253
+ date: {
4175
5254
  type: 'string',
4176
- description: 'Color scheme',
4177
- enum: ['DARK', 'LIGHT']
5255
+ description: 'Life event date (ISO 8601 format)',
5256
+ example: '2024-03-15',
5257
+ format: 'date'
4178
5258
  },
4179
- dateRange: {
5259
+ type: {
4180
5260
  type: 'string',
4181
- description: 'Date range filter',
4182
- example: '1y'
5261
+ description:
5262
+ 'Life event type (user-defined, e.g., "employer", "location")',
5263
+ example: 'employer'
4183
5264
  },
4184
- emergencyFund: {
4185
- type: 'number',
4186
- description: 'Emergency fund amount',
4187
- example: 10000
5265
+ description: {
5266
+ type: 'string',
5267
+ description:
5268
+ 'Life event description. May be an empty string (a valid value distinct from absence).',
5269
+ example: 'Acme Corp'
4188
5270
  },
4189
- 'filters.accounts': {
4190
- description: 'Account filter IDs',
5271
+ meta: {
5272
+ type: 'object',
5273
+ description: 'Product-side metadata (free-form JSON)',
5274
+ example: {
5275
+ note: 'Promotion'
5276
+ }
5277
+ },
5278
+ createdAt: {
5279
+ format: 'date-time',
5280
+ type: 'string',
5281
+ description: 'Creation timestamp',
5282
+ example: '2024-03-15T10:00:00Z'
5283
+ },
5284
+ updatedAt: {
5285
+ format: 'date-time',
5286
+ type: 'string',
5287
+ description:
5288
+ 'Last update timestamp. Also emitted as the ETag response header for If-Match optimistic concurrency.',
5289
+ example: '2024-03-15T10:00:00Z'
5290
+ }
5291
+ },
5292
+ required: [
5293
+ 'id',
5294
+ 'userId',
5295
+ 'date',
5296
+ 'type',
5297
+ 'description',
5298
+ 'meta',
5299
+ 'createdAt',
5300
+ 'updatedAt'
5301
+ ]
5302
+ } as const;
5303
+
5304
+ export const $EventListResponseDto = {
5305
+ type: 'object',
5306
+ properties: {
5307
+ items: {
5308
+ description: 'List of life events',
4191
5309
  type: 'array',
4192
5310
  items: {
4193
- type: 'string'
5311
+ $ref: '#/components/schemas/EventResponseDto'
4194
5312
  }
4195
5313
  },
4196
- 'filters.assetClasses': {
4197
- description: 'Asset class filters',
5314
+ total: {
5315
+ type: 'number',
5316
+ description: 'Total number of life events matching the query',
5317
+ example: 42
5318
+ }
5319
+ },
5320
+ required: ['items', 'total']
5321
+ } as const;
5322
+
5323
+ export const $UpdateBeanEventDto = {
5324
+ type: 'object',
5325
+ properties: {
5326
+ date: {
5327
+ type: 'string',
5328
+ description: 'Life event date (ISO 8601)'
5329
+ },
5330
+ type: {
5331
+ type: 'string',
5332
+ description: 'Life event type (user-defined)'
5333
+ },
5334
+ description: {
5335
+ type: 'string',
5336
+ description:
5337
+ 'Life event description. Empty string is a VALID value (distinct from absence).'
5338
+ },
5339
+ meta: {
5340
+ type: 'object',
5341
+ description: 'Product-side metadata (free-form JSON)'
5342
+ }
5343
+ }
5344
+ } as const;
5345
+
5346
+ export const $OnboardingAccountDto = {
5347
+ type: 'object',
5348
+ properties: {
5349
+ path: {
5350
+ type: 'string',
5351
+ description:
5352
+ 'Account path (Assets/Liabilities only; format validated by the account service)',
5353
+ example: 'Assets:Checking'
5354
+ },
5355
+ currency: {
5356
+ type: 'string',
5357
+ description: 'ISO 4217 currency code (3 letters)',
5358
+ example: 'USD'
5359
+ },
5360
+ openingBalance: {
5361
+ type: 'string',
5362
+ description:
5363
+ 'Opening balance as a non-negative Decimal string (e.g. "1000.00")',
5364
+ example: '1000.00'
5365
+ },
5366
+ platformId: {
5367
+ type: 'string',
5368
+ description:
5369
+ 'Platform ID to bind the account to (references Platform.id); omit for unbound',
5370
+ example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
5371
+ }
5372
+ },
5373
+ required: ['path', 'currency']
5374
+ } as const;
5375
+
5376
+ export const $OnboardingDto = {
5377
+ type: 'object',
5378
+ properties: {
5379
+ accounts: {
5380
+ description: 'Asset/Liability accounts to register with opening balances',
4198
5381
  type: 'array',
4199
5382
  items: {
4200
- type: 'string'
5383
+ $ref: '#/components/schemas/OnboardingAccountDto'
4201
5384
  }
4202
5385
  },
4203
- 'filters.dataSource': {
5386
+ skipAssetRegistration: {
5387
+ type: 'boolean',
5388
+ description:
5389
+ 'Skip asset registration; only bootstrap the core account set',
5390
+ default: false
5391
+ }
5392
+ }
5393
+ } as const;
5394
+
5395
+ export const $ActualBalanceDto = {
5396
+ type: 'object',
5397
+ properties: {
5398
+ amount: {
5399
+ type: 'string',
5400
+ description:
5401
+ 'Actual balance amount as a decimal string (preserves precision for tolerance inference).',
5402
+ example: '1234.56'
5403
+ },
5404
+ ccy: {
5405
+ type: 'string',
5406
+ description: 'Currency code (ISO 4217 or commodity ticker).',
5407
+ example: 'CNY'
5408
+ }
5409
+ },
5410
+ required: ['amount', 'ccy']
5411
+ } as const;
5412
+
5413
+ export const $ComputeReconciliationDto = {
5414
+ type: 'object',
5415
+ properties: {
5416
+ accountId: {
5417
+ type: 'string',
5418
+ description: 'BeanAccount id to reconcile.'
5419
+ },
5420
+ asOfDate: {
5421
+ type: 'string',
5422
+ description: 'Assertion date (ISO 8601, e.g. "2026-07-24").',
5423
+ example: '2026-07-24'
5424
+ },
5425
+ actualBalance: {
5426
+ description: 'Actual balance from the external statement.',
5427
+ allOf: [
5428
+ {
5429
+ $ref: '#/components/schemas/ActualBalanceDto'
5430
+ }
5431
+ ]
5432
+ }
5433
+ },
5434
+ required: ['accountId', 'asOfDate', 'actualBalance']
5435
+ } as const;
5436
+
5437
+ export const $ReconciliationComputeResultDto = {
5438
+ type: 'object',
5439
+ properties: {
5440
+ accountId: {
5441
+ type: 'string'
5442
+ },
5443
+ asOfDate: {
5444
+ type: 'string'
5445
+ },
5446
+ bookBalance: {
5447
+ type: 'string',
5448
+ description: 'System-computed book balance (decimal string).'
5449
+ },
5450
+ actualBalance: {
5451
+ type: 'string',
5452
+ description: 'User-entered actual balance (decimal string).'
5453
+ },
5454
+ currency: {
5455
+ type: 'string'
5456
+ },
5457
+ diff: {
5458
+ type: 'string',
5459
+ description: 'Diff = book − actual (decimal string).'
5460
+ },
5461
+ tolerance: {
5462
+ type: 'string',
5463
+ description: 'Applied tolerance (decimal string).'
5464
+ },
5465
+ withinTolerance: {
5466
+ type: 'boolean',
5467
+ description: 'true when |diff| ≤ tolerance.'
5468
+ },
5469
+ suggestedAction: {
5470
+ type: 'string',
5471
+ enum: ['assert', 'pad'],
5472
+ description:
5473
+ 'Suggested next action: assert when within tolerance, pad otherwise.'
5474
+ }
5475
+ },
5476
+ required: [
5477
+ 'accountId',
5478
+ 'asOfDate',
5479
+ 'bookBalance',
5480
+ 'actualBalance',
5481
+ 'currency',
5482
+ 'diff',
5483
+ 'tolerance',
5484
+ 'withinTolerance',
5485
+ 'suggestedAction'
5486
+ ]
5487
+ } as const;
5488
+
5489
+ export const $AssertReconciliationDto = {
5490
+ type: 'object',
5491
+ properties: {
5492
+ accountId: {
4204
5493
  type: 'string',
4205
- description: 'Data source filter'
5494
+ description: 'BeanAccount id to reconcile.'
4206
5495
  },
4207
- 'filters.symbol': {
5496
+ asOfDate: {
4208
5497
  type: 'string',
4209
- description: 'Symbol filter'
5498
+ description: 'Assertion date (ISO 8601, e.g. "2026-07-24").',
5499
+ example: '2026-07-24'
4210
5500
  },
4211
- 'filters.tags': {
4212
- description: 'Tag filters',
4213
- type: 'array',
4214
- items: {
4215
- type: 'string'
4216
- }
5501
+ actualBalance: {
5502
+ description: 'Actual balance from the external statement.',
5503
+ allOf: [
5504
+ {
5505
+ $ref: '#/components/schemas/ActualBalanceDto'
5506
+ }
5507
+ ]
4217
5508
  },
4218
- isExperimentalFeatures: {
4219
- type: 'boolean',
4220
- description: 'Enable experimental features'
5509
+ tolerance: {
5510
+ type: 'string',
5511
+ description:
5512
+ 'Optional explicit tolerance override. Omit to infer from amount precision (Beancount default).',
5513
+ example: '0.01'
5514
+ }
5515
+ },
5516
+ required: ['accountId', 'asOfDate', 'actualBalance']
5517
+ } as const;
5518
+
5519
+ export const $ReconciliationRecordDto = {
5520
+ type: 'object',
5521
+ properties: {
5522
+ id: {
5523
+ type: 'string'
4221
5524
  },
4222
- isRestrictedView: {
4223
- type: 'boolean',
4224
- description: 'Enable restricted view mode'
5525
+ accountId: {
5526
+ type: 'string'
4225
5527
  },
4226
- language: {
5528
+ date: {
5529
+ type: 'string'
5530
+ },
5531
+ amount: {
4227
5532
  type: 'string',
4228
- description: 'Language code',
4229
- example: 'en'
5533
+ description: 'Asserted (actual) amount.'
4230
5534
  },
4231
- locale: {
5535
+ currency: {
5536
+ type: 'string'
5537
+ },
5538
+ tolerance: {
5539
+ type: 'string'
5540
+ },
5541
+ diffAmount: {
4232
5542
  type: 'string',
4233
- description: 'Locale code',
4234
- example: 'en-US'
5543
+ description: 'book − actual.'
4235
5544
  },
4236
- projectedTotalAmount: {
4237
- type: 'number',
4238
- description: 'Projected total amount',
4239
- example: 1000000
5545
+ diffCurrency: {
5546
+ type: 'string'
4240
5547
  },
4241
- retirementDate: {
5548
+ createdAt: {
5549
+ type: 'string'
5550
+ }
5551
+ },
5552
+ required: ['id', 'accountId', 'date', 'amount', 'currency', 'createdAt']
5553
+ } as const;
5554
+
5555
+ export const $PadReconciliationDto = {
5556
+ type: 'object',
5557
+ properties: {
5558
+ accountId: {
4242
5559
  type: 'string',
4243
- description: 'Retirement date in ISO 8601 format',
4244
- example: '2050-01-01'
5560
+ description: 'BeanAccount id to reconcile.'
4245
5561
  },
4246
- savingsRate: {
4247
- type: 'number',
4248
- description: 'Savings rate percentage',
4249
- example: 0.2
5562
+ asOfDate: {
5563
+ type: 'string',
5564
+ description: 'Assertion date (ISO 8601, e.g. "2026-07-24").',
5565
+ example: '2026-07-24'
4250
5566
  },
4251
- viewMode: {
5567
+ actualBalance: {
5568
+ description: 'Actual balance from the external statement.',
5569
+ allOf: [
5570
+ {
5571
+ $ref: '#/components/schemas/ActualBalanceDto'
5572
+ }
5573
+ ]
5574
+ },
5575
+ sourceAccount: {
4252
5576
  type: 'string',
4253
- description: 'View mode',
4254
- enum: ['DEFAULT', 'ZEN']
5577
+ description:
5578
+ 'Pad source account. Defaults to Equity:Opening-Balances (official Beancount convention).',
5579
+ example: 'Equity:Opening-Balances',
5580
+ default: 'Equity:Opening-Balances'
4255
5581
  }
4256
- }
5582
+ },
5583
+ required: ['accountId', 'asOfDate', 'actualBalance']
4257
5584
  } as const;
4258
5585
 
4259
- export const $UpdatePropertyDto = {
5586
+ export const $PadResultDto = {
4260
5587
  type: 'object',
4261
5588
  properties: {
4262
- value: {
5589
+ transactionId: {
4263
5590
  type: 'string',
4264
- description: 'Property value'
5591
+ description: 'Created pad adjusting transaction id.'
4265
5592
  }
4266
5593
  },
4267
- required: ['value']
5594
+ required: ['transactionId']
4268
5595
  } as const;
4269
5596
 
4270
5597
  export const $FileImportDto = {
@@ -4433,7 +5760,7 @@ export const $IdentifyResultDto = {
4433
5760
  account: {
4434
5761
  type: 'string',
4435
5762
  description: 'Default account used by this importer',
4436
- example: 'Assets:Alipay:Balance'
5763
+ example: 'Assets:CN:Alipay:Balance'
4437
5764
  },
4438
5765
  message: {
4439
5766
  type: 'string',
@@ -4450,7 +5777,7 @@ export const $MapperDefaultsDto = {
4450
5777
  sourceAccount: {
4451
5778
  type: 'string',
4452
5779
  description: 'Source account for transactions (Beancount format)',
4453
- example: 'Assets:Alipay:Balance'
5780
+ example: 'Assets:CN:Alipay:Balance'
4454
5781
  },
4455
5782
  currency: {
4456
5783
  type: 'string',
@@ -4487,7 +5814,7 @@ export const $MapperDefaultsDto = {
4487
5814
  description:
4488
5815
  'Payment method to source account mapping. Maps payment method keywords to Beancount account paths. Used by Alipay/WeChat importers to determine sourceAccount based on payment method (e.g., HuaBei, CreditCard).',
4489
5816
  example: {
4490
- HuaBei: 'Liabilities:Alipay:Huabei',
5817
+ HuaBei: 'Liabilities:CN:CreditLine',
4491
5818
  CreditCard: 'Liabilities:CreditCard'
4492
5819
  }
4493
5820
  }
@@ -4619,8 +5946,9 @@ export const $UpdateMapperDefaultsDto = {
4619
5946
  sourceAccount: {
4620
5947
  type: 'string',
4621
5948
  description: 'Source account for transactions (Beancount format)',
4622
- example: 'Assets:Alipay:Balance',
4623
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
5949
+ example: 'Assets:CN:Alipay:Balance',
5950
+ pattern:
5951
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
4624
5952
  },
4625
5953
  currency: {
4626
5954
  type: 'string',
@@ -4634,20 +5962,22 @@ export const $UpdateMapperDefaultsDto = {
4634
5962
  type: 'string',
4635
5963
  description: 'Default expense account (optional)',
4636
5964
  example: 'Expenses:Unknown',
4637
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
5965
+ pattern:
5966
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
4638
5967
  },
4639
5968
  incomeAccount: {
4640
5969
  type: 'string',
4641
5970
  description: 'Default income account (optional)',
4642
5971
  example: 'Income:Unknown',
4643
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
5972
+ pattern:
5973
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
4644
5974
  },
4645
5975
  methodAccountMapping: {
4646
5976
  type: 'object',
4647
5977
  description:
4648
5978
  'Payment method to source account mapping. Maps payment method keywords to Beancount account paths. Used by Alipay/WeChat importers to determine sourceAccount based on payment method (e.g., HuaBei, CreditCard).',
4649
5979
  example: {
4650
- HuaBei: 'Liabilities:Alipay:Huabei',
5980
+ HuaBei: 'Liabilities:CN:CreditLine',
4651
5981
  CreditCard: 'Liabilities:CreditCard'
4652
5982
  }
4653
5983
  }
@@ -4682,119 +6012,13 @@ export const $UpdateImporterConfigDto = {
4682
6012
  }
4683
6013
  } as const;
4684
6014
 
4685
- export const $CreatePlatformDto = {
4686
- type: 'object',
4687
- properties: {
4688
- name: {
4689
- type: 'string',
4690
- description: 'Platform name',
4691
- example: 'Binance'
4692
- },
4693
- canonical: {
4694
- type: 'string',
4695
- description: 'Platform canonical identifier (lowercase, kebab-case)',
4696
- example: 'binance'
4697
- },
4698
- aliases: {
4699
- description: 'Platform aliases (multi-language names for lookup)',
4700
- example: ['Binance', 'Binance Exchange', 'BNB'],
4701
- type: 'array',
4702
- items: {
4703
- type: 'string'
4704
- }
4705
- },
4706
- url: {
4707
- type: 'string',
4708
- description: 'Platform URL',
4709
- example: 'https://www.binance.com'
4710
- },
4711
- type: {
4712
- type: 'string',
4713
- description: 'Platform type',
4714
- enum: [
4715
- 'BANK',
4716
- 'BROKERAGE',
4717
- 'CRYPTO_EXCHANGE',
4718
- 'PAYMENT',
4719
- 'INVESTMENT',
4720
- 'INSURANCE',
4721
- 'OTHER'
4722
- ],
4723
- example: 'CRYPTO_EXCHANGE'
4724
- },
4725
- logoUrl: {
4726
- type: 'string',
4727
- description: 'Platform logo URL',
4728
- example: 'https://example.com/logos/binance.png'
4729
- },
4730
- isActive: {
4731
- type: 'boolean',
4732
- description: 'Whether the platform is active',
4733
- default: true
4734
- }
4735
- },
4736
- required: ['name', 'canonical', 'aliases', 'url', 'type']
4737
- } as const;
4738
-
4739
- export const $UpdatePlatformDto = {
4740
- type: 'object',
4741
- properties: {
4742
- name: {
4743
- type: 'string',
4744
- description: 'Platform name',
4745
- example: 'Binance'
4746
- },
4747
- canonical: {
4748
- type: 'string',
4749
- description: 'Platform canonical identifier (lowercase, kebab-case)',
4750
- example: 'binance'
4751
- },
4752
- aliases: {
4753
- description: 'Platform aliases (multi-language names for lookup)',
4754
- example: ['Binance', 'Binance Exchange', 'BNB'],
4755
- type: 'array',
4756
- items: {
4757
- type: 'string'
4758
- }
4759
- },
4760
- url: {
4761
- type: 'string',
4762
- description: 'Platform URL',
4763
- example: 'https://www.binance.com'
4764
- },
4765
- type: {
4766
- type: 'string',
4767
- description: 'Platform type',
4768
- enum: [
4769
- 'BANK',
4770
- 'BROKERAGE',
4771
- 'CRYPTO_EXCHANGE',
4772
- 'PAYMENT',
4773
- 'INVESTMENT',
4774
- 'INSURANCE',
4775
- 'OTHER'
4776
- ],
4777
- example: 'CRYPTO_EXCHANGE'
4778
- },
4779
- logoUrl: {
4780
- type: 'string',
4781
- description: 'Platform logo URL',
4782
- example: 'https://example.com/logos/binance.png'
4783
- },
4784
- isActive: {
4785
- type: 'boolean',
4786
- description: 'Whether the platform is active'
4787
- }
4788
- }
4789
- } as const;
4790
-
4791
6015
  export const $ProviderSyncConfigDto = {
4792
6016
  type: 'object',
4793
6017
  properties: {
4794
6018
  sourceAccount: {
4795
6019
  type: 'string',
4796
6020
  description: 'Source account for the first posting',
4797
- example: 'Assets:Bank:Chase'
6021
+ example: 'Assets:US:Chase:Checking'
4798
6022
  },
4799
6023
  defaultCurrency: {
4800
6024
  type: 'string',
@@ -4803,26 +6027,29 @@ export const $ProviderSyncConfigDto = {
4803
6027
  },
4804
6028
  defaultExpenseAccount: {
4805
6029
  type: 'string',
4806
- description: 'Default expense account for the second posting',
6030
+ description:
6031
+ 'Default expense account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
4807
6032
  example: 'Expenses:Unknown'
4808
6033
  },
4809
6034
  defaultIncomeAccount: {
4810
6035
  type: 'string',
4811
- description: 'Default income account for the second posting',
6036
+ description:
6037
+ 'Default income account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
4812
6038
  example: 'Income:Unknown'
4813
6039
  },
4814
6040
  filterPending: {
4815
6041
  type: 'boolean',
4816
6042
  description: 'Filter pending transactions',
4817
6043
  default: true
6044
+ },
6045
+ externalAccountId: {
6046
+ type: 'string',
6047
+ description:
6048
+ 'External account ID for per-batch providers (e.g. GoCardless). Overrides sourceAccount when an ExternalAccountLink mapping exists.',
6049
+ example: 'acc_gocardless_001'
4818
6050
  }
4819
6051
  },
4820
- required: [
4821
- 'sourceAccount',
4822
- 'defaultCurrency',
4823
- 'defaultExpenseAccount',
4824
- 'defaultIncomeAccount'
4825
- ]
6052
+ required: ['sourceAccount', 'defaultCurrency']
4826
6053
  } as const;
4827
6054
 
4828
6055
  export const $ProviderSyncDto = {
@@ -4927,11 +6154,99 @@ export const $SupportedProvidersResponseDto = {
4927
6154
  ],
4928
6155
  type: 'array',
4929
6156
  items: {
4930
- type: 'string'
6157
+ type: 'string'
6158
+ }
6159
+ }
6160
+ },
6161
+ required: ['providers']
6162
+ } as const;
6163
+
6164
+ export const $CreateExternalAccountLinkDto = {
6165
+ type: 'object',
6166
+ properties: {
6167
+ provider: {
6168
+ type: 'string',
6169
+ enum: [
6170
+ 'plaid',
6171
+ 'teller',
6172
+ 'truelayer',
6173
+ 'gocardless',
6174
+ 'simplefin',
6175
+ 'yodlee',
6176
+ 'beancount-direct',
6177
+ 'parsed-bill'
6178
+ ],
6179
+ example: 'plaid',
6180
+ description: 'Open Banking provider (whitelist)'
6181
+ },
6182
+ externalAccountId: {
6183
+ type: 'string',
6184
+ example: 'acc-plaid-001',
6185
+ description: 'External account ID from the provider'
6186
+ },
6187
+ beanAccountId: {
6188
+ type: 'string',
6189
+ example: '550e8400-e29b-41d4-a716-446655440000',
6190
+ description: 'Target BeanAccount ID (must belong to the JWT user)'
6191
+ }
6192
+ },
6193
+ required: ['provider', 'externalAccountId', 'beanAccountId']
6194
+ } as const;
6195
+
6196
+ export const $ExternalAccountLinkResponseDto = {
6197
+ type: 'object',
6198
+ properties: {
6199
+ id: {
6200
+ type: 'string'
6201
+ },
6202
+ provider: {
6203
+ type: 'string'
6204
+ },
6205
+ externalAccountId: {
6206
+ type: 'string'
6207
+ },
6208
+ beanAccountId: {
6209
+ type: 'string'
6210
+ },
6211
+ isActive: {
6212
+ type: 'boolean'
6213
+ },
6214
+ createdAt: {
6215
+ type: 'string'
6216
+ },
6217
+ updatedAt: {
6218
+ type: 'string'
6219
+ }
6220
+ },
6221
+ required: [
6222
+ 'id',
6223
+ 'provider',
6224
+ 'externalAccountId',
6225
+ 'beanAccountId',
6226
+ 'isActive',
6227
+ 'createdAt',
6228
+ 'updatedAt'
6229
+ ]
6230
+ } as const;
6231
+
6232
+ export const $ExternalAccountLinkListResponseDto = {
6233
+ type: 'object',
6234
+ properties: {
6235
+ items: {
6236
+ type: 'array',
6237
+ items: {
6238
+ $ref: '#/components/schemas/ExternalAccountLinkResponseDto'
4931
6239
  }
6240
+ },
6241
+ total: {
6242
+ type: 'number'
6243
+ },
6244
+ provider: {
6245
+ type: 'string',
6246
+ description: 'Filter by provider (query param)'
4932
6247
  }
4933
6248
  },
4934
- required: ['providers']
6249
+ required: ['items', 'total']
4935
6250
  } as const;
4936
6251
 
4937
6252
  export const $ParserTelemetryReportDto = {
@@ -4944,15 +6259,105 @@ export const $UncoveredFormatMissDto = {
4944
6259
  properties: {}
4945
6260
  } as const;
4946
6261
 
6262
+ export const $ClientParsedDataDto = {
6263
+ type: 'object',
6264
+ properties: {
6265
+ amount: {
6266
+ type: 'number',
6267
+ description: 'Transaction amount',
6268
+ example: 35
6269
+ },
6270
+ currency: {
6271
+ type: 'string',
6272
+ description: 'Currency code',
6273
+ example: 'CNY'
6274
+ },
6275
+ date: {
6276
+ type: 'string',
6277
+ description: 'Transaction date (ISO 8601)',
6278
+ example: '2026-08-15'
6279
+ },
6280
+ payee: {
6281
+ type: 'string',
6282
+ description: 'Payee/merchant name',
6283
+ example: 'Starbucks'
6284
+ },
6285
+ narration: {
6286
+ type: 'string',
6287
+ description: 'Transaction narration'
6288
+ },
6289
+ category: {
6290
+ type: 'string',
6291
+ description: 'Category slug',
6292
+ example: 'food_restaurant'
6293
+ },
6294
+ incomeType: {
6295
+ type: 'string',
6296
+ description: 'Income type',
6297
+ example: 'Salary'
6298
+ },
6299
+ incomeSource: {
6300
+ type: 'string',
6301
+ description: 'Income source',
6302
+ example: 'Anthropic Inc.'
6303
+ },
6304
+ symbol: {
6305
+ type: 'string',
6306
+ description: 'Security symbol code (e.g., 600519, AAPL)',
6307
+ example: 'AAPL'
6308
+ },
6309
+ quantity: {
6310
+ type: 'number',
6311
+ description: 'Quantity of shares/units',
6312
+ example: 100
6313
+ },
6314
+ price: {
6315
+ type: 'number',
6316
+ description: 'Unit price per share/unit',
6317
+ example: 1900
6318
+ },
6319
+ investmentAction: {
6320
+ type: 'string',
6321
+ description: 'Investment action',
6322
+ enum: ['buy', 'sell'],
6323
+ example: 'buy'
6324
+ },
6325
+ paymentSource: {
6326
+ type: 'string',
6327
+ description: 'Payment source: asset (default) or liability (credit card)',
6328
+ enum: ['asset', 'liability'],
6329
+ example: 'asset'
6330
+ },
6331
+ liabilityHint: {
6332
+ type: 'string',
6333
+ description: 'Liability account hint (CreditCard/Huabei/Baitiao)',
6334
+ example: 'CreditCard'
6335
+ },
6336
+ warning: {
6337
+ type: 'string',
6338
+ description:
6339
+ 'Display-only warning from the prior response; accepted but ignored.',
6340
+ example: 'Cross-currency settlement applies.'
6341
+ }
6342
+ }
6343
+ } as const;
6344
+
4947
6345
  export const $ProcessNlpDto = {
4948
6346
  type: 'object',
4949
6347
  properties: {
4950
6348
  message: {
4951
6349
  type: 'string',
4952
- description: 'Natural language text describing a transaction (Chinese)',
4953
- example: 'yesterday Starbucks spent 35 yuan',
6350
+ description:
6351
+ 'Natural language text describing a transaction. Optional when `confirm` is true (structured confirm); otherwise required.',
6352
+ example: 'Starbucks 35',
4954
6353
  maxLength: 500
4955
6354
  },
6355
+ confirm: {
6356
+ type: 'boolean',
6357
+ description:
6358
+ 'Structured confirm signal — bypasses NL confirm-word matching when true. Send parsedData field edits alongside. The NL word-list path is the fallback.',
6359
+ example: true
6360
+ },
4956
6361
  sessionId: {
4957
6362
  type: 'string',
4958
6363
  description:
@@ -4960,17 +6365,32 @@ export const $ProcessNlpDto = {
4960
6365
  example: 'session_abc123'
4961
6366
  },
4962
6367
  parsedData: {
4963
- type: 'object',
4964
6368
  description:
4965
6369
  'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
4966
6370
  example: {
4967
6371
  amount: 35,
4968
6372
  currency: 'CNY',
4969
6373
  payee: 'Starbucks'
4970
- }
6374
+ },
6375
+ allOf: [
6376
+ {
6377
+ $ref: '#/components/schemas/ClientParsedDataDto'
6378
+ }
6379
+ ]
6380
+ },
6381
+ selectedRuleId: {
6382
+ type: 'string',
6383
+ description:
6384
+ 'confirm_rule echo-back: rule id selected from the prior confirm_rule response (matchedRule.id or alternatives[i].ruleId). Applied directly when the session is confirming_rule — no NL re-parse.',
6385
+ example: 'rule_abc123'
6386
+ },
6387
+ selectedAccount: {
6388
+ type: 'string',
6389
+ description:
6390
+ 'confirm_account echo-back: account path selected from the prior confirm_account response (suggestedAccount, similarAccounts[i], or a typed path). Applied directly when the session is confirming_account — no NL re-parse.',
6391
+ example: 'Expenses:Food:Coffee'
4971
6392
  }
4972
- },
4973
- required: ['message']
6393
+ }
4974
6394
  } as const;
4975
6395
 
4976
6396
  export const $NlpTransactionInfoDto = {
@@ -5292,7 +6712,8 @@ export const $NlpAccountConfirmationDataDto = {
5292
6712
  },
5293
6713
  suggestedAccount: {
5294
6714
  type: 'string',
5295
- description: 'Suggested replacement account',
6715
+ description:
6716
+ 'Suggested replacement account (omitted when no clear candidate)',
5296
6717
  example: 'Expenses:Food:Drinks'
5297
6718
  },
5298
6719
  similarAccounts: {
@@ -5314,7 +6735,6 @@ export const $NlpAccountConfirmationDataDto = {
5314
6735
  },
5315
6736
  required: [
5316
6737
  'invalidAccount',
5317
- 'suggestedAccount',
5318
6738
  'similarAccounts',
5319
6739
  'errorMessage',
5320
6740
  'transactionContext'
@@ -5483,11 +6903,12 @@ export const $NlpSuggestedAccountDto = {
5483
6903
  account: {
5484
6904
  type: 'string',
5485
6905
  description: 'Suggested account path',
5486
- example: 'Assets:Bank:Checking'
6906
+ example: 'Assets:Checking'
5487
6907
  },
5488
6908
  confidence: {
5489
6909
  type: 'number',
5490
- description: 'Confidence score for this suggestion (0-1)',
6910
+ description:
6911
+ 'Confidence score for this suggestion (0-1). Present = predicted (confirm/confirm_rule/confirm_account); omitted = actual persisted account (created). (#586)',
5491
6912
  example: 0.9
5492
6913
  }
5493
6914
  },
@@ -5523,23 +6944,31 @@ export const $NlpDefaultAccountsDto = {
5523
6944
  properties: {
5524
6945
  asset: {
5525
6946
  type: 'string',
5526
- description: 'Default asset account',
5527
- example: 'Assets:Bank:Checking'
6947
+ description:
6948
+ 'Default OPEN asset account (MRU when multiple), or null when none/ambiguous',
6949
+ example: 'Assets:Checking',
6950
+ nullable: true
5528
6951
  },
5529
6952
  expense: {
5530
6953
  type: 'string',
5531
- description: 'Default expense account',
5532
- example: 'Expenses:Uncategorized'
6954
+ description:
6955
+ 'Default OPEN expense account (MRU when multiple), or null when none/ambiguous',
6956
+ example: 'Expenses:Food:Coffee',
6957
+ nullable: true
5533
6958
  },
5534
6959
  income: {
5535
6960
  type: 'string',
5536
- description: 'Default income account',
5537
- example: 'Income:Uncategorized'
6961
+ description:
6962
+ 'Default OPEN income account (MRU when multiple), or null when none/ambiguous',
6963
+ example: 'Income:Salary',
6964
+ nullable: true
5538
6965
  },
5539
6966
  liability: {
5540
6967
  type: 'string',
5541
- description: 'Default liability account',
5542
- example: 'Liabilities:CreditCard'
6968
+ description:
6969
+ 'Default OPEN liability account (MRU when multiple), or null when none/ambiguous',
6970
+ example: 'Liabilities:CreditCard',
6971
+ nullable: true
5543
6972
  }
5544
6973
  },
5545
6974
  required: ['asset', 'expense', 'income', 'liability']
@@ -5564,7 +6993,8 @@ export const $NlpResponseDto = {
5564
6993
  'confirm_rule',
5565
6994
  'confirm_account',
5566
6995
  'confirm_payee',
5567
- 'cancel'
6996
+ 'cancel',
6997
+ 'aborted'
5568
6998
  ]
5569
6999
  },
5570
7000
  intent: {
@@ -5578,7 +7008,7 @@ export const $NlpResponseDto = {
5578
7008
  type: 'string',
5579
7009
  description:
5580
7010
  'Asset sub-type (only present when intent is "asset"). Determines which asset-related form to render.',
5581
- enum: ['transfer', 'banking', 'investment'],
7011
+ enum: ['transfer', 'banking', 'investment', 'lend', 'lend_collect'],
5582
7012
  example: 'investment'
5583
7013
  },
5584
7014
  liabilitySubType: {
@@ -5686,81 +7116,342 @@ export const $NlpResponseDto = {
5686
7116
  }
5687
7117
  ]
5688
7118
  },
5689
- payeeData: {
7119
+ payeeData: {
7120
+ description:
7121
+ 'Payee confirmation data (when action is "confirm_payee"). Contains information about medium/low confidence payee match for user confirmation.',
7122
+ allOf: [
7123
+ {
7124
+ $ref: '#/components/schemas/NlpPayeeConfirmationDataDto'
7125
+ }
7126
+ ]
7127
+ },
7128
+ confidence: {
7129
+ type: 'number',
7130
+ description: 'Overall confidence score (0-1)',
7131
+ example: 0.85
7132
+ },
7133
+ confidenceThreshold: {
7134
+ type: 'number',
7135
+ description:
7136
+ 'Confidence threshold for automatic creation (default: 0.75). When confidence < threshold, action will be "confirm" requiring user verification.',
7137
+ example: 0.75
7138
+ },
7139
+ recurringMatch: {
7140
+ description:
7141
+ 'Recurring transaction match info (when action is "created"). Contains match details when transaction matches a pending expected transaction.',
7142
+ allOf: [
7143
+ {
7144
+ $ref: '#/components/schemas/RecurringMatchInfoDto'
7145
+ }
7146
+ ]
7147
+ },
7148
+ recurringSuggestion: {
7149
+ description:
7150
+ 'Recurring rule creation suggestion (when action is "created"). Contains suggestion to create a recurring rule based on detected patterns. Only present when no existing rule matched and similar historical transactions were found.',
7151
+ allOf: [
7152
+ {
7153
+ $ref: '#/components/schemas/RecurringSuggestionDto'
7154
+ }
7155
+ ]
7156
+ },
7157
+ suggestedAccounts: {
7158
+ description:
7159
+ 'Suggested accounts for this transaction (#586). confirm/confirm_rule/confirm_account: predicted (source/destination carry confidence); created: actual persisted accounts (confidence omitted). confirm_account destination is the suggested replacement, never the invalid account.',
7160
+ allOf: [
7161
+ {
7162
+ $ref: '#/components/schemas/NlpSuggestedAccountsDto'
7163
+ }
7164
+ ]
7165
+ },
7166
+ defaultAccounts: {
7167
+ description:
7168
+ 'Default fallback accounts for the user/region (#586). v1 returns universal constants; per-user personalization is planned.',
7169
+ allOf: [
7170
+ {
7171
+ $ref: '#/components/schemas/NlpDefaultAccountsDto'
7172
+ }
7173
+ ]
7174
+ }
7175
+ },
7176
+ required: ['status', 'action']
7177
+ } as const;
7178
+
7179
+ export const $PlatformListItemDto = {
7180
+ type: 'object',
7181
+ properties: {
7182
+ id: {
7183
+ type: 'string',
7184
+ description: 'Global platform ID'
7185
+ },
7186
+ name: {
7187
+ type: 'string',
7188
+ description: 'Platform name'
7189
+ },
7190
+ url: {
7191
+ type: 'string',
7192
+ description: 'Platform URL'
7193
+ },
7194
+ type: {
7195
+ type: 'string',
7196
+ description: 'Platform type',
7197
+ enum: [
7198
+ 'BANK',
7199
+ 'BROKERAGE',
7200
+ 'CRYPTO_EXCHANGE',
7201
+ 'PAYMENT',
7202
+ 'INVESTMENT',
7203
+ 'INSURANCE',
7204
+ 'OTHER'
7205
+ ]
7206
+ },
7207
+ canonical: {
7208
+ type: 'string',
7209
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7210
+ },
7211
+ suggestedSegment: {
7212
+ type: 'string',
7213
+ description:
7214
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7215
+ },
7216
+ logoUrl: {
7217
+ type: 'string',
7218
+ description: 'Logo URL',
7219
+ nullable: true
7220
+ },
7221
+ countryCode: {
7222
+ type: 'string',
7223
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7224
+ example: 'CN',
7225
+ nullable: true
7226
+ },
7227
+ category: {
7228
+ type: 'string',
7229
+ description:
7230
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7231
+ nullable: true,
7232
+ example: 'DigitalWallet'
7233
+ },
7234
+ isBound: {
7235
+ type: 'boolean',
7236
+ description: 'Whether user has accounts using this platform'
7237
+ }
7238
+ },
7239
+ required: [
7240
+ 'id',
7241
+ 'name',
7242
+ 'url',
7243
+ 'type',
7244
+ 'canonical',
7245
+ 'suggestedSegment',
7246
+ 'logoUrl',
7247
+ 'countryCode',
7248
+ 'category',
7249
+ 'isBound'
7250
+ ]
7251
+ } as const;
7252
+
7253
+ export const $PlatformMatchResultDto = {
7254
+ type: 'object',
7255
+ properties: {
7256
+ id: {
7257
+ type: 'string',
7258
+ description: 'Global platform ID'
7259
+ },
7260
+ name: {
7261
+ type: 'string',
7262
+ description: 'Platform name (e.g., "ICBC")'
7263
+ },
7264
+ canonical: {
7265
+ type: 'string',
7266
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7267
+ },
7268
+ type: {
7269
+ type: 'string',
7270
+ description: 'Platform type',
7271
+ enum: [
7272
+ 'BANK',
7273
+ 'BROKERAGE',
7274
+ 'CRYPTO_EXCHANGE',
7275
+ 'PAYMENT',
7276
+ 'INVESTMENT',
7277
+ 'INSURANCE',
7278
+ 'OTHER'
7279
+ ]
7280
+ },
7281
+ suggestedSegment: {
7282
+ type: 'string',
5690
7283
  description:
5691
- 'Payee confirmation data (when action is "confirm_payee"). Contains information about medium/low confidence payee match for user confirmation.',
5692
- allOf: [
5693
- {
5694
- $ref: '#/components/schemas/NlpPayeeConfirmationDataDto'
5695
- }
5696
- ]
7284
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
5697
7285
  },
5698
- confidence: {
5699
- type: 'number',
5700
- description: 'Overall confidence score (0-1)',
5701
- example: 0.85
7286
+ logoUrl: {
7287
+ type: 'string',
7288
+ description: 'Logo URL',
7289
+ nullable: true
5702
7290
  },
5703
- confidenceThreshold: {
5704
- type: 'number',
5705
- description:
5706
- 'Confidence threshold for automatic creation (default: 0.75). When confidence < threshold, action will be "confirm" requiring user verification.',
5707
- example: 0.75
7291
+ countryCode: {
7292
+ type: 'string',
7293
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7294
+ example: 'CN',
7295
+ nullable: true
5708
7296
  },
5709
- recurringMatch: {
7297
+ category: {
7298
+ type: 'string',
5710
7299
  description:
5711
- 'Recurring transaction match info (when action is "created"). Contains match details when transaction matches a pending expected transaction.',
5712
- allOf: [
5713
- {
5714
- $ref: '#/components/schemas/RecurringMatchInfoDto'
5715
- }
5716
- ]
7300
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7301
+ nullable: true,
7302
+ example: 'DigitalWallet'
5717
7303
  },
5718
- recurringSuggestion: {
5719
- description:
5720
- 'Recurring rule creation suggestion (when action is "created"). Contains suggestion to create a recurring rule based on detected patterns. Only present when no existing rule matched and similar historical transactions were found.',
5721
- allOf: [
5722
- {
5723
- $ref: '#/components/schemas/RecurringSuggestionDto'
5724
- }
5725
- ]
7304
+ matchType: {
7305
+ type: 'string',
7306
+ description: "How this row matched: 'exact' > 'prefix' > 'substring'",
7307
+ enum: ['exact', 'prefix', 'substring']
7308
+ }
7309
+ },
7310
+ required: [
7311
+ 'id',
7312
+ 'name',
7313
+ 'canonical',
7314
+ 'type',
7315
+ 'suggestedSegment',
7316
+ 'logoUrl',
7317
+ 'countryCode',
7318
+ 'category',
7319
+ 'matchType'
7320
+ ]
7321
+ } as const;
7322
+
7323
+ export const $PlatformMatchResponseDto = {
7324
+ type: 'object',
7325
+ properties: {
7326
+ platforms: {
7327
+ description: 'Ranked matches, best tier first (at most 10 rows)',
7328
+ type: 'array',
7329
+ items: {
7330
+ $ref: '#/components/schemas/PlatformMatchResultDto'
7331
+ }
5726
7332
  },
5727
- suggestedAccounts: {
7333
+ matchType: {
7334
+ type: 'string',
5728
7335
  description:
5729
- 'Suggested accounts for this transaction. Contains recommended source and destination accounts based on the detected intent and rules.',
5730
- allOf: [
5731
- {
5732
- $ref: '#/components/schemas/NlpSuggestedAccountsDto'
5733
- }
5734
- ]
7336
+ "Overall match quality — top row's tier, or 'none' when no hits",
7337
+ enum: ['none', 'exact', 'prefix', 'substring']
5735
7338
  },
5736
- defaultAccounts: {
5737
- description:
5738
- 'Default accounts for the user/region. These are fallback accounts used when no specific suggestion is available.',
5739
- allOf: [
5740
- {
5741
- $ref: '#/components/schemas/NlpDefaultAccountsDto'
5742
- }
5743
- ]
7339
+ total: {
7340
+ type: 'number',
7341
+ description: 'Total matches before LIMIT (truncation transparency)'
7342
+ },
7343
+ hasMore: {
7344
+ type: 'boolean',
7345
+ description: 'true when total > platforms.length (more matches exist)'
5744
7346
  }
5745
7347
  },
5746
- required: ['status', 'action']
7348
+ required: ['platforms', 'matchType', 'total', 'hasMore']
5747
7349
  } as const;
5748
7350
 
5749
- export const $BalanceByCurrencyDto = {
7351
+ export const $CreatePlatformDto = {
5750
7352
  type: 'object',
5751
7353
  properties: {
5752
- currency: {
7354
+ name: {
5753
7355
  type: 'string',
5754
- description: 'ISO 4217 currency code',
5755
- example: 'CNY'
7356
+ description: 'Platform name',
7357
+ example: 'Binance'
5756
7358
  },
5757
- balance: {
7359
+ canonical: {
5758
7360
  type: 'string',
5759
- description: 'Balance amount',
5760
- example: '50000.00'
7361
+ description: 'Platform canonical identifier (lowercase, kebab-case)',
7362
+ example: 'binance'
7363
+ },
7364
+ aliases: {
7365
+ description: 'Platform aliases (multi-language names for lookup)',
7366
+ example: ['Binance', 'Binance Exchange', 'BNB'],
7367
+ type: 'array',
7368
+ items: {
7369
+ type: 'string'
7370
+ }
7371
+ },
7372
+ url: {
7373
+ type: 'string',
7374
+ description: 'Platform URL',
7375
+ example: 'https://www.binance.com'
7376
+ },
7377
+ type: {
7378
+ type: 'string',
7379
+ description: 'Platform type',
7380
+ enum: [
7381
+ 'BANK',
7382
+ 'BROKERAGE',
7383
+ 'CRYPTO_EXCHANGE',
7384
+ 'PAYMENT',
7385
+ 'INVESTMENT',
7386
+ 'INSURANCE',
7387
+ 'OTHER'
7388
+ ],
7389
+ example: 'CRYPTO_EXCHANGE'
7390
+ },
7391
+ logoUrl: {
7392
+ type: 'string',
7393
+ description: 'Platform logo URL',
7394
+ example: 'https://example.com/logos/binance.png'
7395
+ },
7396
+ isActive: {
7397
+ type: 'boolean',
7398
+ description: 'Whether the platform is active',
7399
+ default: true
5761
7400
  }
5762
7401
  },
5763
- required: ['currency', 'balance']
7402
+ required: ['name', 'canonical', 'aliases', 'url', 'type']
7403
+ } as const;
7404
+
7405
+ export const $UpdatePlatformDto = {
7406
+ type: 'object',
7407
+ properties: {
7408
+ name: {
7409
+ type: 'string',
7410
+ description: 'Platform name',
7411
+ example: 'Binance'
7412
+ },
7413
+ canonical: {
7414
+ type: 'string',
7415
+ description: 'Platform canonical identifier (lowercase, kebab-case)',
7416
+ example: 'binance'
7417
+ },
7418
+ aliases: {
7419
+ description: 'Platform aliases (multi-language names for lookup)',
7420
+ example: ['Binance', 'Binance Exchange', 'BNB'],
7421
+ type: 'array',
7422
+ items: {
7423
+ type: 'string'
7424
+ }
7425
+ },
7426
+ url: {
7427
+ type: 'string',
7428
+ description: 'Platform URL',
7429
+ example: 'https://www.binance.com'
7430
+ },
7431
+ type: {
7432
+ type: 'string',
7433
+ description: 'Platform type',
7434
+ enum: [
7435
+ 'BANK',
7436
+ 'BROKERAGE',
7437
+ 'CRYPTO_EXCHANGE',
7438
+ 'PAYMENT',
7439
+ 'INVESTMENT',
7440
+ 'INSURANCE',
7441
+ 'OTHER'
7442
+ ],
7443
+ example: 'CRYPTO_EXCHANGE'
7444
+ },
7445
+ logoUrl: {
7446
+ type: 'string',
7447
+ description: 'Platform logo URL',
7448
+ example: 'https://example.com/logos/binance.png'
7449
+ },
7450
+ isActive: {
7451
+ type: 'boolean',
7452
+ description: 'Whether the platform is active'
7453
+ }
7454
+ }
5764
7455
  } as const;
5765
7456
 
5766
7457
  export const $NetWorthByCurrencyDto = {
@@ -5832,28 +7523,6 @@ export const $ConvertedNetWorthDto = {
5832
7523
  ]
5833
7524
  } as const;
5834
7525
 
5835
- export const $ExchangeRateWarningDto = {
5836
- type: 'object',
5837
- properties: {
5838
- type: {
5839
- type: 'string',
5840
- description: 'Warning type',
5841
- example: 'MISSING_EXCHANGE_RATE'
5842
- },
5843
- currency: {
5844
- type: 'string',
5845
- description: 'Currency without exchange rate',
5846
- example: 'EUR'
5847
- },
5848
- totalAmount: {
5849
- type: 'string',
5850
- description: 'Total amount affected',
5851
- example: '1000.00'
5852
- }
5853
- },
5854
- required: ['type', 'currency', 'totalAmount']
5855
- } as const;
5856
-
5857
7526
  export const $NetWorthResponseDto = {
5858
7527
  type: 'object',
5859
7528
  properties: {
@@ -5939,7 +7608,7 @@ export const $AccountItemDto = {
5939
7608
  name: {
5940
7609
  type: 'string',
5941
7610
  description: 'Full account name',
5942
- example: 'Assets:Bank:CMB:Savings'
7611
+ example: 'Assets:CN:CMB:Savings'
5943
7612
  },
5944
7613
  displayName: {
5945
7614
  type: 'string',
@@ -5951,41 +7620,102 @@ export const $AccountItemDto = {
5951
7620
  description: 'Account balance',
5952
7621
  example: '50000.00'
5953
7622
  },
5954
- currency: {
5955
- type: 'string',
5956
- description: 'Currency code',
5957
- example: 'CNY'
7623
+ currency: {
7624
+ type: 'string',
7625
+ description: 'Currency code',
7626
+ example: 'CNY'
7627
+ },
7628
+ convertedBalance: {
7629
+ type: 'string',
7630
+ description:
7631
+ 'FX-converted balance in base currency; omitted when not convertible',
7632
+ example: '50000.00'
7633
+ }
7634
+ },
7635
+ required: ['id', 'name', 'displayName', 'balance', 'currency']
7636
+ } as const;
7637
+
7638
+ export const $PlatformGroupDto = {
7639
+ type: 'object',
7640
+ properties: {
7641
+ platformId: {
7642
+ type: 'string',
7643
+ description: 'Platform ID'
7644
+ },
7645
+ platformName: {
7646
+ type: 'string',
7647
+ description: 'Platform display name',
7648
+ example: 'CMB Bank'
7649
+ },
7650
+ accounts: {
7651
+ description: 'Accounts within this platform',
7652
+ type: 'array',
7653
+ items: {
7654
+ $ref: '#/components/schemas/AccountItemDto'
7655
+ }
7656
+ },
7657
+ totalBalance: {
7658
+ type: 'string',
7659
+ description: 'FX-converted total balance in base currency',
7660
+ example: '100000.00'
7661
+ },
7662
+ balanceByCurrency: {
7663
+ description: 'Raw (unconverted) balances grouped by currency',
7664
+ type: 'array',
7665
+ items: {
7666
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
7667
+ }
7668
+ },
7669
+ convertedBalance: {
7670
+ type: 'string',
7671
+ description:
7672
+ 'Converted balance in base currency (omitted when no currency is convertible)',
7673
+ example: '100000.00'
7674
+ },
7675
+ sharePct: {
7676
+ type: 'number',
7677
+ description:
7678
+ 'Share of the grand converted total (0-100); 0 when grand total is 0',
7679
+ example: 42.5
5958
7680
  }
5959
7681
  },
5960
- required: ['id', 'name', 'displayName', 'balance', 'currency']
7682
+ required: [
7683
+ 'platformId',
7684
+ 'platformName',
7685
+ 'accounts',
7686
+ 'totalBalance',
7687
+ 'balanceByCurrency',
7688
+ 'sharePct'
7689
+ ]
5961
7690
  } as const;
5962
7691
 
5963
- export const $PlatformGroupDto = {
7692
+ export const $AccountExchangeRateWarningDto = {
5964
7693
  type: 'object',
5965
7694
  properties: {
5966
- platformId: {
7695
+ type: {
5967
7696
  type: 'string',
5968
- description: 'Platform ID'
7697
+ description: 'Warning type',
7698
+ example: 'MISSING_EXCHANGE_RATE'
5969
7699
  },
5970
- platformName: {
7700
+ currency: {
5971
7701
  type: 'string',
5972
- description: 'Platform display name',
5973
- example: 'CMB Bank'
7702
+ description: 'Currency without exchange rate',
7703
+ example: 'USD'
5974
7704
  },
5975
7705
  accounts: {
5976
- description: 'Accounts within this platform',
7706
+ description: 'Affected account paths',
5977
7707
  type: 'array',
5978
7708
  items: {
5979
- $ref: '#/components/schemas/AccountItemDto'
7709
+ type: 'string'
5980
7710
  }
5981
7711
  },
5982
- totalBalance: {
7712
+ totalAmount: {
5983
7713
  type: 'string',
5984
- description: 'Total balance across all accounts in platform',
5985
- example: '100000.00'
7714
+ description: 'Total amount in this currency',
7715
+ example: '5000.00'
5986
7716
  }
5987
7717
  },
5988
- required: ['platformId', 'platformName', 'accounts', 'totalBalance']
7718
+ required: ['type', 'currency', 'accounts', 'totalAmount']
5989
7719
  } as const;
5990
7720
 
5991
7721
  export const $AccountsSummaryDto = {
@@ -5998,9 +7728,21 @@ export const $AccountsSummaryDto = {
5998
7728
  totalPlatforms: {
5999
7729
  type: 'number',
6000
7730
  description: 'Total number of platforms'
7731
+ },
7732
+ baseCurrency: {
7733
+ type: 'string',
7734
+ description: 'Base currency for conversion',
7735
+ example: 'CNY'
7736
+ },
7737
+ warnings: {
7738
+ description: 'Per-account exchange rate warnings',
7739
+ type: 'array',
7740
+ items: {
7741
+ $ref: '#/components/schemas/AccountExchangeRateWarningDto'
7742
+ }
6001
7743
  }
6002
7744
  },
6003
- required: ['totalAccounts', 'totalPlatforms']
7745
+ required: ['totalAccounts', 'totalPlatforms', 'baseCurrency']
6004
7746
  } as const;
6005
7747
 
6006
7748
  export const $AccountsResponseDto = {
@@ -6035,7 +7777,7 @@ export const $AccountItemWithAssetClassDto = {
6035
7777
  name: {
6036
7778
  type: 'string',
6037
7779
  description: 'Full account name',
6038
- example: 'Assets:Bank:CMB:Savings'
7780
+ example: 'Assets:CN:CMB:Savings'
6039
7781
  },
6040
7782
  displayName: {
6041
7783
  type: 'string',
@@ -6052,6 +7794,12 @@ export const $AccountItemWithAssetClassDto = {
6052
7794
  description: 'Currency code',
6053
7795
  example: 'CNY'
6054
7796
  },
7797
+ convertedBalance: {
7798
+ type: 'string',
7799
+ description:
7800
+ 'FX-converted balance in base currency; omitted when not convertible',
7801
+ example: '50000.00'
7802
+ },
6055
7803
  assetClass: {
6056
7804
  type: 'string',
6057
7805
  description: 'Asset class',
@@ -6131,35 +7879,6 @@ export const $AssetClassGroupDto = {
6131
7879
  required: ['assetClass', 'accounts', 'balanceByCurrency']
6132
7880
  } as const;
6133
7881
 
6134
- export const $AccountExchangeRateWarningDto = {
6135
- type: 'object',
6136
- properties: {
6137
- type: {
6138
- type: 'string',
6139
- description: 'Warning type',
6140
- example: 'MISSING_EXCHANGE_RATE'
6141
- },
6142
- currency: {
6143
- type: 'string',
6144
- description: 'Currency without exchange rate',
6145
- example: 'USD'
6146
- },
6147
- accounts: {
6148
- description: 'Affected account paths',
6149
- type: 'array',
6150
- items: {
6151
- type: 'string'
6152
- }
6153
- },
6154
- totalAmount: {
6155
- type: 'string',
6156
- description: 'Total amount in this currency',
6157
- example: '5000.00'
6158
- }
6159
- },
6160
- required: ['type', 'currency', 'accounts', 'totalAmount']
6161
- } as const;
6162
-
6163
7882
  export const $AssetClassSummaryDto = {
6164
7883
  type: 'object',
6165
7884
  properties: {
@@ -6233,7 +7952,7 @@ export const $HoldingAssetClassAccountSliceDto = {
6233
7952
  accountPath: {
6234
7953
  type: 'string',
6235
7954
  description: 'Full account path',
6236
- example: 'Assets:US:Investments:Brokerage'
7955
+ example: 'Assets:US:Fidelity:Brokerage'
6237
7956
  },
6238
7957
  accountCurrency: {
6239
7958
  type: 'string',
@@ -6439,6 +8158,101 @@ export const $CashFlowResponseDto = {
6439
8158
  ]
6440
8159
  } as const;
6441
8160
 
8161
+ export const $CategoryGroupDto = {
8162
+ type: 'object',
8163
+ properties: {
8164
+ category: {
8165
+ type: 'string',
8166
+ description:
8167
+ 'Functional category (account-path Group segment); regional and universal account paths merge under it',
8168
+ example: 'Food'
8169
+ },
8170
+ totalExpense: {
8171
+ type: 'string',
8172
+ description:
8173
+ 'Converted total for this category in base currency (expense amount when flow=expense, income amount when flow=income)',
8174
+ example: '1200.00'
8175
+ },
8176
+ sharePct: {
8177
+ type: 'number',
8178
+ description: 'Share of grand total (0-100); 0 when grand total is 0',
8179
+ example: 42.5
8180
+ },
8181
+ balanceByCurrency: {
8182
+ description: 'Raw (unconverted) expense per currency',
8183
+ type: 'array',
8184
+ items: {
8185
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
8186
+ }
8187
+ },
8188
+ convertedBalance: {
8189
+ type: 'string',
8190
+ description:
8191
+ 'Converted total in base currency (omitted when FX missing for all currencies in this category)',
8192
+ example: '1200.00'
8193
+ }
8194
+ },
8195
+ required: ['category', 'totalExpense', 'sharePct', 'balanceByCurrency']
8196
+ } as const;
8197
+
8198
+ export const $ExpensesByCategorySummaryDto = {
8199
+ type: 'object',
8200
+ properties: {
8201
+ totalExpense: {
8202
+ type: 'string',
8203
+ description:
8204
+ 'Total across all categories, converted (convertible categories only); expense totals when flow=expense, income totals when flow=income',
8205
+ example: '5000.00'
8206
+ },
8207
+ categoryCount: {
8208
+ type: 'number',
8209
+ description: 'Number of categories',
8210
+ example: 8
8211
+ }
8212
+ },
8213
+ required: ['totalExpense', 'categoryCount']
8214
+ } as const;
8215
+
8216
+ export const $ExpensesByCategoryResponseDto = {
8217
+ type: 'object',
8218
+ properties: {
8219
+ period: {
8220
+ type: 'string',
8221
+ description: 'Period requested',
8222
+ example: '1m'
8223
+ },
8224
+ baseCurrency: {
8225
+ type: 'string',
8226
+ description: 'Base currency for converted values',
8227
+ example: 'CNY'
8228
+ },
8229
+ groups: {
8230
+ description:
8231
+ 'Expense groups by functional category, sorted by converted total desc',
8232
+ type: 'array',
8233
+ items: {
8234
+ $ref: '#/components/schemas/CategoryGroupDto'
8235
+ }
8236
+ },
8237
+ summary: {
8238
+ description: 'Summary statistics',
8239
+ allOf: [
8240
+ {
8241
+ $ref: '#/components/schemas/ExpensesByCategorySummaryDto'
8242
+ }
8243
+ ]
8244
+ },
8245
+ warnings: {
8246
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
8247
+ type: 'array',
8248
+ items: {
8249
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
8250
+ }
8251
+ }
8252
+ },
8253
+ required: ['period', 'baseCurrency', 'groups', 'summary']
8254
+ } as const;
8255
+
6442
8256
  export const $MonetaryDto = {
6443
8257
  type: 'object',
6444
8258
  properties: {
@@ -6728,334 +8542,378 @@ export const $HoldingPnlResponseDto = {
6728
8542
  required: ['asOfDate', 'baseCurrency', 'method', 'rows', 'warnings']
6729
8543
  } as const;
6730
8544
 
6731
- export const $CreateBeanPriceDto = {
8545
+ export const $AnonymousLoginDto = {
6732
8546
  type: 'object',
6733
8547
  properties: {
6734
- currency: {
6735
- type: 'string',
6736
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
6737
- example: 'USD'
6738
- },
6739
- quoteCurrency: {
8548
+ accessToken: {
6740
8549
  type: 'string',
6741
- description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
6742
- example: 'CNY'
6743
- },
6744
- amount: {
6745
- type: 'number',
6746
- description:
6747
- 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
6748
- example: 175.5,
6749
- minimum: 0
6750
- },
6751
- date: {
8550
+ description: 'Access token for anonymous login'
8551
+ }
8552
+ },
8553
+ required: ['accessToken']
8554
+ } as const;
8555
+
8556
+ export const $AnonymousLoginResponseDto = {
8557
+ type: 'object',
8558
+ properties: {
8559
+ authToken: {
6752
8560
  type: 'string',
6753
- description: 'Price date (ISO 8601 format)',
6754
- example: '2024-11-05'
6755
- },
6756
- metadata: {
6757
- type: 'object',
6758
- description:
6759
- 'Metadata (validated by Zod schema, max field lengths enforced)',
6760
- example: {
6761
- source: 'MANUAL',
6762
- note: 'Bank valuation report',
6763
- confidence: 0.95
6764
- }
8561
+ description: 'JWT auth token',
8562
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
6765
8563
  }
6766
8564
  },
6767
- required: ['currency', 'quoteCurrency', 'amount', 'date']
8565
+ required: ['authToken']
6768
8566
  } as const;
6769
8567
 
6770
- export const $PriceResponseDto = {
8568
+ export const $ParserContributionMetaDto = {
6771
8569
  type: 'object',
6772
8570
  properties: {
6773
- id: {
8571
+ institution: {
6774
8572
  type: 'string',
6775
- description: 'Unique identifier',
6776
- example: 'uuid-123-456'
8573
+ description: 'Institution slug (lowercase kebab-case)',
8574
+ pattern: '^[a-z0-9]+(-[a-z0-9]+)*$',
8575
+ example: 'icbc'
6777
8576
  },
6778
- userId: {
8577
+ region: {
6779
8578
  type: 'string',
6780
- description: 'User ID (owner of the price)',
6781
- example: 'user-123'
8579
+ enum: [
8580
+ 'cn',
8581
+ 'us',
8582
+ 'de',
8583
+ 'fr',
8584
+ 'gb',
8585
+ 'hk',
8586
+ 'jp',
8587
+ 'sg',
8588
+ 'au',
8589
+ 'ca',
8590
+ 'other'
8591
+ ]
6782
8592
  },
6783
- currency: {
8593
+ accountType: {
6784
8594
  type: 'string',
6785
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
6786
- example: 'BTC'
8595
+ enum: ['checking', 'savings', 'credit', 'debit', 'investment']
6787
8596
  },
6788
- quoteCurrency: {
8597
+ format: {
6789
8598
  type: 'string',
6790
- description: 'Quote currency (pricing currency, e.g., USD, CNY)',
6791
- example: 'USD'
6792
- },
6793
- amount: {
6794
- type: 'number',
6795
- description:
6796
- 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
6797
- example: 50000
8599
+ enum: ['csv', 'xlsx', 'pdf', 'ofx', 'qif']
6798
8600
  },
6799
- date: {
8601
+ institutionDisplayName: {
6800
8602
  type: 'string',
6801
- description:
6802
- 'Price date (ISO 8601 format). Represents the date this price was valid.',
6803
- example: '2024-01-01',
6804
- format: 'date'
8603
+ example: '中国工商银行'
6805
8604
  },
6806
- meta: {
6807
- type: 'object',
6808
- description:
6809
- 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
6810
- example: {
6811
- source: 'MANUAL',
6812
- note: 'User-defined price',
6813
- confidence: 1
6814
- }
8605
+ encoding: {
8606
+ type: 'string',
8607
+ example: 'utf-8'
6815
8608
  },
6816
- createdAt: {
6817
- format: 'date-time',
8609
+ delimiter: {
6818
8610
  type: 'string',
6819
- description: 'Creation timestamp',
6820
- example: '2024-11-03T10:00:00Z'
8611
+ description: 'CSV delimiter character: ",", ";", "\\t" or "|"'
6821
8612
  },
6822
- updatedAt: {
6823
- format: 'date-time',
8613
+ headerRows: {
8614
+ type: 'number',
8615
+ default: 1,
8616
+ description: 'Header row count; the client omits the field when it is 1'
8617
+ },
8618
+ notes: {
6824
8619
  type: 'string',
6825
- description: 'Last update timestamp',
6826
- example: '2024-11-03T10:00:00Z'
8620
+ maxLength: 2000
6827
8621
  }
6828
8622
  },
6829
- required: [
6830
- 'id',
6831
- 'userId',
6832
- 'currency',
6833
- 'quoteCurrency',
6834
- 'amount',
6835
- 'date',
6836
- 'meta',
6837
- 'createdAt',
6838
- 'updatedAt'
6839
- ]
8623
+ required: ['institution', 'region', 'accountType', 'format']
6840
8624
  } as const;
6841
8625
 
6842
- export const $PriceListResponseDto = {
8626
+ export const $ParserContributionSamplesDto = {
6843
8627
  type: 'object',
6844
8628
  properties: {
6845
- items: {
6846
- description: 'List of prices',
8629
+ rows: {
8630
+ description:
8631
+ 'Client-sanitized sample rows (key = column name, value = cell)',
8632
+ type: 'array',
8633
+ items: {
8634
+ type: 'string'
8635
+ }
8636
+ },
8637
+ rawHeaders: {
6847
8638
  type: 'array',
6848
8639
  items: {
6849
- $ref: '#/components/schemas/PriceResponseDto'
8640
+ type: 'string'
6850
8641
  }
6851
- },
6852
- total: {
6853
- type: 'number',
6854
- description: 'Total number of prices',
6855
- example: 42
6856
8642
  }
6857
8643
  },
6858
- required: ['items', 'total']
8644
+ required: ['rows']
6859
8645
  } as const;
6860
8646
 
6861
- export const $UpdateBeanPriceDto = {
8647
+ export const $FieldHintDto = {
6862
8648
  type: 'object',
6863
8649
  properties: {
6864
- currency: {
8650
+ columnName: {
6865
8651
  type: 'string',
6866
- description: 'Currency being priced'
8652
+ example: '交易日期'
6867
8653
  },
6868
- quoteCurrency: {
8654
+ format: {
6869
8655
  type: 'string',
6870
- description: 'Quote currency (pricing currency)'
6871
- },
6872
- amount: {
6873
- type: 'number',
6874
- description: 'Price amount (MUST be >= 0 per Beancount spec)',
6875
- minimum: 0
8656
+ description: 'Date format, e.g. yyyy-MM-dd HH:mm',
8657
+ example: 'yyyy-MM-dd'
6876
8658
  },
6877
- date: {
8659
+ signConvention: {
6878
8660
  type: 'string',
6879
- description: 'Price date (ISO 8601 format)'
8661
+ enum: ['negative-expense', 'positive-expense', 'separate-columns']
6880
8662
  },
6881
- metadata: {
6882
- type: 'object',
6883
- description: 'Metadata'
8663
+ creditColumn: {
8664
+ type: 'string'
8665
+ },
8666
+ debitColumn: {
8667
+ type: 'string'
6884
8668
  }
6885
- }
8669
+ },
8670
+ required: ['columnName']
6886
8671
  } as const;
6887
8672
 
6888
- export const $CurrencyBalanceDto = {
8673
+ export const $ParserContributionFieldHintsDto = {
6889
8674
  type: 'object',
6890
8675
  properties: {
6891
- currency: {
6892
- type: 'string',
6893
- description: 'ISO 4217 currency code',
6894
- example: 'CNY'
8676
+ date: {
8677
+ $ref: '#/components/schemas/FieldHintDto'
8678
+ },
8679
+ amount: {
8680
+ $ref: '#/components/schemas/FieldHintDto'
8681
+ },
8682
+ description: {
8683
+ $ref: '#/components/schemas/FieldHintDto'
6895
8684
  },
6896
8685
  balance: {
6897
- type: 'string',
6898
- description: 'Balance amount',
6899
- example: '500000.00'
8686
+ $ref: '#/components/schemas/FieldHintDto'
8687
+ },
8688
+ payee: {
8689
+ $ref: '#/components/schemas/FieldHintDto'
8690
+ },
8691
+ reference: {
8692
+ $ref: '#/components/schemas/FieldHintDto'
8693
+ },
8694
+ category: {
8695
+ $ref: '#/components/schemas/FieldHintDto'
6900
8696
  }
6901
8697
  },
6902
- required: ['currency', 'balance']
8698
+ required: ['date', 'amount']
6903
8699
  } as const;
6904
8700
 
6905
- export const $TimeSeriesPointDto = {
8701
+ export const $ExpectedTransactionDto = {
6906
8702
  type: 'object',
6907
8703
  properties: {
6908
8704
  date: {
6909
8705
  type: 'string',
6910
- description: 'Date in YYYY-MM-DD format',
6911
- example: '2024-06-15'
8706
+ example: '2026-08-01'
6912
8707
  },
6913
- value: {
8708
+ amount: {
8709
+ type: 'number',
8710
+ example: -45.5
8711
+ },
8712
+ description: {
6914
8713
  type: 'string',
6915
- description: 'Value at this date (in base currency)',
6916
- example: '500000.00'
8714
+ example: '星巴克-***店'
6917
8715
  },
6918
- change: {
6919
- type: 'object',
6920
- description: 'Change from previous point',
6921
- example: '5000.00'
8716
+ payee: {
8717
+ type: 'string'
6922
8718
  },
6923
- byCurrency: {
6924
- description: 'Multi-currency breakdown for this point',
8719
+ category: {
8720
+ type: 'string'
8721
+ }
8722
+ },
8723
+ required: ['date', 'amount', 'description']
8724
+ } as const;
8725
+
8726
+ export const $ParserContributionExamplesDto = {
8727
+ type: 'object',
8728
+ properties: {
8729
+ expectedTransactions: {
6925
8730
  type: 'array',
6926
8731
  items: {
6927
- $ref: '#/components/schemas/CurrencyBalanceDto'
8732
+ $ref: '#/components/schemas/ExpectedTransactionDto'
6928
8733
  }
6929
8734
  }
6930
8735
  },
6931
- required: ['date', 'value']
8736
+ required: ['expectedTransactions']
6932
8737
  } as const;
6933
8738
 
6934
- export const $TrendSummaryDto = {
8739
+ export const $ParserContributionRequestDto = {
6935
8740
  type: 'object',
6936
8741
  properties: {
6937
- startValue: {
6938
- type: 'string',
6939
- description: 'Value at start of period',
6940
- example: '450000.00'
8742
+ meta: {
8743
+ $ref: '#/components/schemas/ParserContributionMetaDto'
6941
8744
  },
6942
- endValue: {
6943
- type: 'string',
6944
- description: 'Value at end of period',
6945
- example: '500000.00'
8745
+ samples: {
8746
+ $ref: '#/components/schemas/ParserContributionSamplesDto'
6946
8747
  },
6947
- totalChange: {
6948
- type: 'string',
6949
- description: 'Total change over period',
6950
- example: '50000.00'
8748
+ fieldHints: {
8749
+ $ref: '#/components/schemas/ParserContributionFieldHintsDto'
6951
8750
  },
6952
- totalChangePercentage: {
6953
- type: 'string',
6954
- description: 'Total change percentage',
6955
- example: '+11.11%'
8751
+ examples: {
8752
+ description: 'Omitted entirely by the client when empty',
8753
+ allOf: [
8754
+ {
8755
+ $ref: '#/components/schemas/ParserContributionExamplesDto'
8756
+ }
8757
+ ]
6956
8758
  }
6957
8759
  },
6958
- required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
8760
+ required: ['meta', 'samples', 'fieldHints']
6959
8761
  } as const;
6960
8762
 
6961
- export const $MultiCurrencyPointDto = {
8763
+ export const $ParserContributionRelayResponseDto = {
6962
8764
  type: 'object',
6963
8765
  properties: {
6964
- date: {
8766
+ issueUrl: {
6965
8767
  type: 'string',
6966
- description: 'Date in YYYY-MM-DD format',
6967
- example: '2024-06-15'
8768
+ example: 'https://github.com/fire-la/parsers/issues/42'
6968
8769
  },
6969
- byCurrency: {
6970
- description: 'Balances by currency',
6971
- type: 'array',
6972
- items: {
6973
- $ref: '#/components/schemas/CurrencyBalanceDto'
6974
- }
8770
+ issueNumber: {
8771
+ type: 'number',
8772
+ example: 42
6975
8773
  }
6976
8774
  },
6977
- required: ['date', 'byCurrency']
8775
+ required: ['issueUrl', 'issueNumber']
6978
8776
  } as const;
6979
8777
 
6980
- export const $PortfolioTrendsResponseDto = {
8778
+ export const $SymbolSearchResultDto = {
6981
8779
  type: 'object',
6982
8780
  properties: {
6983
- series: {
6984
- description: 'Time series data points',
6985
- type: 'array',
6986
- items: {
6987
- $ref: '#/components/schemas/TimeSeriesPointDto'
6988
- }
8781
+ symbol: {
8782
+ type: 'string',
8783
+ example: 'AAPL'
6989
8784
  },
6990
- summary: {
6991
- description: 'Period summary',
6992
- allOf: [
6993
- {
6994
- $ref: '#/components/schemas/TrendSummaryDto'
6995
- }
6996
- ]
8785
+ name: {
8786
+ type: 'object',
8787
+ example: 'Apple Inc.',
8788
+ nullable: true
6997
8789
  },
6998
- period: {
6999
- type: 'string',
7000
- description: 'Period requested',
7001
- example: '6m'
8790
+ exchange: {
8791
+ type: 'object',
8792
+ example: 'US',
8793
+ nullable: true
7002
8794
  },
7003
- granularity: {
7004
- type: 'string',
7005
- description: 'Data granularity',
7006
- example: 'month'
8795
+ assetType: {
8796
+ type: 'object',
8797
+ description: 'OpenBB asset_type (e.g. stock, etf)',
8798
+ example: 'stock',
8799
+ nullable: true
7007
8800
  },
7008
- currency: {
7009
- type: 'string',
7010
- description: 'Base currency for converted values',
7011
- example: 'CNY'
8801
+ assetClass: {
8802
+ type: 'object',
8803
+ description: 'IGN asset class (region.types.ts ASSET_CLASSES)',
8804
+ example: 'EQUITY',
8805
+ nullable: true
7012
8806
  },
7013
- byCurrency: {
7014
- description:
7015
- 'Multi-currency time series (each point has currency breakdown)',
7016
- type: 'array',
7017
- items: {
7018
- $ref: '#/components/schemas/MultiCurrencyPointDto'
7019
- }
8807
+ assetSubClass: {
8808
+ type: 'object',
8809
+ description: 'IGN asset sub-class (region.types.ts ASSET_SUB_CLASSES)',
8810
+ example: 'STOCK',
8811
+ nullable: true
7020
8812
  },
7021
- warnings: {
7022
- description: 'Exchange rate warnings',
7023
- type: 'array',
7024
- items: {
7025
- $ref: '#/components/schemas/ExchangeRateWarningDto'
7026
- }
8813
+ currency: {
8814
+ type: 'object',
8815
+ description: 'Trading currency (extra_data or inferred from exchange)',
8816
+ example: 'USD',
8817
+ nullable: true
7027
8818
  }
7028
8819
  },
7029
- required: ['series', 'summary', 'period', 'granularity', 'currency']
7030
- } as const;
7031
-
7032
- export const $GenerateSnapshotBody = {
7033
- type: 'object',
7034
- properties: {}
7035
- } as const;
7036
-
7037
- export const $GenerateSnapshotResponse = {
7038
- type: 'object',
7039
- properties: {}
7040
- } as const;
7041
-
7042
- export const $BackfillSnapshotsBody = {
7043
- type: 'object',
7044
- properties: {}
7045
- } as const;
7046
-
7047
- export const $BackfillSnapshotsResponse = {
7048
- type: 'object',
7049
- properties: {}
8820
+ required: ['symbol']
7050
8821
  } as const;
7051
8822
 
7052
- export const $AnonymousLoginDto = {
8823
+ export const $SymbolQuoteDto = {
7053
8824
  type: 'object',
7054
8825
  properties: {
7055
- accessToken: {
8826
+ symbol: {
7056
8827
  type: 'string',
7057
- description: 'Access token for anonymous login'
8828
+ example: 'AAPL'
8829
+ },
8830
+ name: {
8831
+ type: 'object',
8832
+ example: 'Apple Inc.',
8833
+ nullable: true
8834
+ },
8835
+ exchange: {
8836
+ type: 'object',
8837
+ example: 'US',
8838
+ nullable: true
8839
+ },
8840
+ assetType: {
8841
+ type: 'object',
8842
+ description: 'OpenBB asset_type',
8843
+ example: 'stock',
8844
+ nullable: true
8845
+ },
8846
+ assetClass: {
8847
+ type: 'object',
8848
+ description: 'IGN asset class',
8849
+ example: 'EQUITY',
8850
+ nullable: true
8851
+ },
8852
+ assetSubClass: {
8853
+ type: 'object',
8854
+ description: 'IGN asset sub-class',
8855
+ example: 'STOCK',
8856
+ nullable: true
8857
+ },
8858
+ currency: {
8859
+ type: 'object',
8860
+ description: 'Trading currency (extra_data or inferred from exchange)',
8861
+ example: 'USD',
8862
+ nullable: true
8863
+ },
8864
+ price: {
8865
+ type: 'object',
8866
+ description: 'Latest price (Decimal string)',
8867
+ example: '189.84',
8868
+ nullable: true
8869
+ },
8870
+ priceDate: {
8871
+ type: 'object',
8872
+ description: 'Date the price was observed (ISO yyyy-MM-dd)',
8873
+ example: '2026-08-05',
8874
+ nullable: true
8875
+ },
8876
+ changePercent: {
8877
+ type: 'object',
8878
+ description:
8879
+ 'Change vs previous close, in percentage points (1.7 == 1.7%). openbb stores change_percent as a normalized decimal; this exposes percentage points for frontend convenience.',
8880
+ example: 1.7,
8881
+ nullable: true
8882
+ },
8883
+ prevClose: {
8884
+ type: 'object',
8885
+ description: 'Previous close (Decimal string)',
8886
+ nullable: true
8887
+ },
8888
+ open: {
8889
+ type: 'object',
8890
+ description: 'Day open (Decimal string)',
8891
+ nullable: true
8892
+ },
8893
+ high: {
8894
+ type: 'object',
8895
+ description: 'Day high (Decimal string)',
8896
+ nullable: true
8897
+ },
8898
+ low: {
8899
+ type: 'object',
8900
+ description: 'Day low (Decimal string)',
8901
+ nullable: true
8902
+ },
8903
+ volume: {
8904
+ type: 'object',
8905
+ description: 'Day volume (Decimal string)',
8906
+ nullable: true
8907
+ },
8908
+ yearHigh: {
8909
+ type: 'object',
8910
+ description: '52-week high (Decimal string)',
8911
+ nullable: true
8912
+ },
8913
+ yearLow: {
8914
+ type: 'object',
8915
+ description: '52-week low (Decimal string)',
8916
+ nullable: true
7058
8917
  }
7059
- },
7060
- required: ['accessToken']
8918
+ }
7061
8919
  } as const;