@firela/api-types 0.0.0-canary.97006feb → 0.0.0-canary.977cafd5

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.
@@ -51,6 +51,14 @@ export const $CreateAccountDto = {
51
51
  description: 'Icon identifier (overrides template)',
52
52
  example: 'bank-custom'
53
53
  },
54
+ displayName: {
55
+ type: 'string',
56
+ description:
57
+ 'User-set display name override (omit/null = keep the derived name)',
58
+ nullable: true,
59
+ maxLength: 50,
60
+ example: 'Salary card'
61
+ },
54
62
  openDirectiveMeta: {
55
63
  type: 'object',
56
64
  description:
@@ -88,6 +96,49 @@ export const $AccountResponseDto = {
88
96
  enum: ['Assets', 'Liabilities', 'Income', 'Expenses', 'Equity'],
89
97
  example: 'Assets'
90
98
  },
99
+ assetSubClass: {
100
+ type: 'string',
101
+ description:
102
+ '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.',
103
+ enum: [
104
+ 'DEPOSIT',
105
+ 'CASH',
106
+ 'MONEY_MARKET_FUND',
107
+ 'STOCK',
108
+ 'ETF',
109
+ 'MUTUAL_FUND',
110
+ 'EQUITY_COMPENSATION',
111
+ 'GOVERNMENT_BOND',
112
+ 'CORPORATE_BOND',
113
+ 'BOND_FUND',
114
+ 'PRIMARY_RESIDENCE',
115
+ 'INVESTMENT_PROPERTY',
116
+ 'REIT',
117
+ 'GOLD',
118
+ 'SILVER',
119
+ 'PRECIOUS_METAL',
120
+ 'PRECIOUS_METAL_FUND',
121
+ 'COMMODITY',
122
+ 'COMMODITY_FUND',
123
+ 'CRYPTOCURRENCY',
124
+ 'RETIREMENT_ACCOUNT',
125
+ 'HEALTH_ACCOUNT',
126
+ 'EDUCATION_ACCOUNT',
127
+ 'INSURANCE',
128
+ 'PRIVATE_EQUITY',
129
+ 'HEDGE_FUND',
130
+ 'COLLECTIBLES',
131
+ 'MORTGAGE',
132
+ 'STUDENT_LOAN',
133
+ 'CREDIT_CARD',
134
+ 'PERSONAL_LOAN',
135
+ 'ACCOUNTS_PAYABLE',
136
+ 'TAX_PAYABLE',
137
+ 'OTHER'
138
+ ],
139
+ nullable: true,
140
+ example: 'STOCK'
141
+ },
91
142
  status: {
92
143
  type: 'string',
93
144
  description: 'Account status',
@@ -138,7 +189,8 @@ export const $AccountResponseDto = {
138
189
  },
139
190
  displayName: {
140
191
  type: 'string',
141
- description: 'Localized display name (ADR-0114, read-time projection)',
192
+ description:
193
+ 'Display name with precedence: user-set name (#762) > ADR-0114 localized name > path leaf (read-time projection)',
142
194
  example: 'Checking'
143
195
  },
144
196
  icon: {
@@ -154,7 +206,7 @@ export const $AccountResponseDto = {
154
206
  }
155
207
  },
156
208
  platformId: {
157
- type: 'object',
209
+ type: 'string',
158
210
  description: 'Platform ID (null if unbound)',
159
211
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
160
212
  },
@@ -234,6 +286,14 @@ export const $UpdateAccountDto = {
234
286
  description: 'Icon identifier',
235
287
  example: 'bank-custom'
236
288
  },
289
+ displayName: {
290
+ type: 'string',
291
+ description:
292
+ 'User-set display name override (null = clear the override and fall back to the derived name, omit = unchanged)',
293
+ nullable: true,
294
+ maxLength: 50,
295
+ example: 'Salary card'
296
+ },
237
297
  openDirectiveMeta: {
238
298
  type: 'object',
239
299
  description:
@@ -334,16 +394,43 @@ export const $AccountStandardResponseDto = {
334
394
  },
335
395
  name: {
336
396
  type: 'string',
337
- description: 'Short localized display name',
397
+ description:
398
+ 'Short display name. Universal rows project to the request locale (Accept-Language); regional rows keep the authored native name — mixed-language by design (ADR-0131 class P vs class A).',
338
399
  example: 'Housing Fund'
339
400
  },
401
+ aliases: {
402
+ description:
403
+ 'Authored market-language alternative names delivered verbatim (not localized copy, not xlf-managed, not locale-projected). Flat string[] per ADR-0129 D1; ADR-0131 class A.',
404
+ example: ['Alipay', 'WeChat Pay'],
405
+ type: 'array',
406
+ items: {
407
+ type: 'string'
408
+ }
409
+ },
410
+ searchTerms: {
411
+ description:
412
+ 'Locale-projected search synonyms (e.g. the zh bank-card / debit-card everyday terms for the checking account). Pure-locale projection — absent when the locale has no seeded synonyms; English fallback rides the authored aliases field. Search-only vocabulary, not the NLP routing corpus (#698, ADR-0131 fourth-class adjudication).',
413
+ example: ['yinhangka', 'jiejika'],
414
+ type: 'array',
415
+ items: {
416
+ type: 'string'
417
+ }
418
+ },
419
+ currency: {
420
+ type: 'string',
421
+ description:
422
+ 'Product denomination as a 3-letter ISO 4217 code, authored market data delivered verbatim (not localized, not xlf-managed). ADR-0131 class A. Absent = single-currency not asserted — consumers fall back to their own region currency (#714).',
423
+ example: 'HKD'
424
+ },
340
425
  description: {
341
426
  type: 'string',
342
- description: 'Account description (stable semantics only)',
427
+ description:
428
+ 'Account description (stable semantics only). Mixed-language contract: universal rows project to the request locale via the accountDesc xlf axis with an en fallback (ADR-0131 class P, ADR-0132; unseeded locales falling back to English are expected); regional rows deliver the authored market language (ADR-0131 class A, verbatim, never xlf-managed).',
343
429
  example: 'ICBC checking account for daily transactions'
344
430
  },
345
431
  tags: {
346
- description: 'Account tags for categorization',
432
+ description:
433
+ 'Account tags for categorization — structured metadata delivered verbatim (not localized, not xlf-managed). ADR-0131 class A.',
347
434
  example: ['bank', 'checking', 'primary'],
348
435
  type: 'array',
349
436
  items: {
@@ -354,9 +441,47 @@ export const $AccountStandardResponseDto = {
354
441
  type: 'string',
355
442
  description: 'Icon identifier for UI display',
356
443
  example: 'bank-icbc'
444
+ },
445
+ productCategory: {
446
+ type: 'string',
447
+ description:
448
+ 'Onboarding product category (coarse grouping derived from assetSubClass)',
449
+ enum: [
450
+ 'cash',
451
+ 'investment',
452
+ 'credit_card',
453
+ 'loan',
454
+ 'payable_tax',
455
+ 'other'
456
+ ],
457
+ example: 'investment'
458
+ },
459
+ assetClass: {
460
+ type: 'string',
461
+ description:
462
+ 'Asset class (LIQUIDITY/EQUITY/.../LIABILITY), derived at read time from classification rules',
463
+ enum: [
464
+ 'LIQUIDITY',
465
+ 'EQUITY',
466
+ 'FIXED_INCOME',
467
+ 'PRECIOUS_METALS',
468
+ 'COMMODITY',
469
+ 'INSURANCE',
470
+ 'ALTERNATIVE_INVESTMENT',
471
+ 'PERSONAL_ASSETS',
472
+ 'LIABILITY',
473
+ 'REAL_ESTATE',
474
+ 'INDEX'
475
+ ]
476
+ },
477
+ assetSubClass: {
478
+ type: 'string',
479
+ description:
480
+ 'Asset sub-class (product type, derived at read time from classification rules)',
481
+ example: 'STOCK'
357
482
  }
358
483
  },
359
- required: ['path', 'type', 'description', 'tags', 'icon']
484
+ required: ['path', 'type', 'description', 'tags', 'icon', 'productCategory']
360
485
  } as const;
361
486
 
362
487
  export const $AccountStandardListResponseDto = {
@@ -417,7 +542,10 @@ export const $RegionConfigDto = {
417
542
  },
418
543
  locale: {
419
544
  type: 'string',
420
- example: 'de-DE'
545
+ example: 'de-DE',
546
+ pattern: '^[a-z]{2,8}-[A-Z]{2}$',
547
+ description:
548
+ "Region-qualified BCP-47 tag whose region subtag equals the region's own ISO 3166-1 code (e.g., ja-JP, zh-CN, zh-HK)"
421
549
  }
422
550
  },
423
551
  required: ['currency', 'dateFormat', 'locale']
@@ -430,6 +558,12 @@ export const $RegionInfoDto = {
430
558
  type: 'string',
431
559
  example: 'de'
432
560
  },
561
+ open: {
562
+ type: 'boolean',
563
+ example: true,
564
+ description:
565
+ 'Whether the region is open (has a ready regional account template). Not-yet-open regions still return identity metadata and degrade to the universal-only catalog.'
566
+ },
433
567
  displayName: {
434
568
  type: 'string',
435
569
  example: 'Germany'
@@ -448,7 +582,7 @@ export const $RegionInfoDto = {
448
582
  $ref: '#/components/schemas/RegionConfigDto'
449
583
  }
450
584
  },
451
- required: ['code', 'displayName', 'chain', 'config']
585
+ required: ['code', 'open', 'displayName', 'chain', 'config']
452
586
  } as const;
453
587
 
454
588
  export const $RegionsMetadataResponseDto = {
@@ -695,7 +829,7 @@ export const $PostingResponseDto = {
695
829
  units: {
696
830
  type: 'string',
697
831
  description:
698
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
832
+ '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.',
699
833
  example: '100.50'
700
834
  },
701
835
  currency: {
@@ -1061,7 +1195,7 @@ export const $PostingDetailDto = {
1061
1195
  units: {
1062
1196
  type: 'string',
1063
1197
  description:
1064
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
1198
+ '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.',
1065
1199
  example: '100.50'
1066
1200
  },
1067
1201
  currency: {
@@ -1245,6 +1379,147 @@ export const $TransactionDetailDto = {
1245
1379
  ]
1246
1380
  } as const;
1247
1381
 
1382
+ export const $TransactionListItemDto = {
1383
+ type: 'object',
1384
+ properties: {
1385
+ id: {
1386
+ type: 'string',
1387
+ description: 'Transaction ID',
1388
+ example: 'clh1234567890abcdef'
1389
+ },
1390
+ date: {
1391
+ type: 'string',
1392
+ description: 'Transaction date',
1393
+ example: '2024-11-28'
1394
+ },
1395
+ flag: {
1396
+ type: 'string',
1397
+ description: 'Transaction flag',
1398
+ enum: [
1399
+ 'CLEARED',
1400
+ 'PENDING',
1401
+ 'PADDING',
1402
+ 'SUMMARIZE',
1403
+ 'TRANSFER',
1404
+ 'CONVERSIONS'
1405
+ ],
1406
+ example: 'CLEARED'
1407
+ },
1408
+ customFlag: {
1409
+ type: 'string',
1410
+ description: 'Custom flag (if not using standard flags)',
1411
+ example: 'R'
1412
+ },
1413
+ payee: {
1414
+ type: 'string',
1415
+ description: 'Payee name',
1416
+ example: 'Whole Foods Market'
1417
+ },
1418
+ narration: {
1419
+ type: 'string',
1420
+ description: 'Transaction narration',
1421
+ example: 'Grocery shopping'
1422
+ },
1423
+ tags: {
1424
+ description: 'Transaction tags',
1425
+ example: ['groceries'],
1426
+ type: 'array',
1427
+ items: {
1428
+ type: 'string'
1429
+ }
1430
+ },
1431
+ links: {
1432
+ description: 'Transaction links',
1433
+ example: ['invoice-2024-001'],
1434
+ type: 'array',
1435
+ items: {
1436
+ type: 'string'
1437
+ }
1438
+ },
1439
+ meta: {
1440
+ type: 'object',
1441
+ description: 'Transaction metadata'
1442
+ },
1443
+ status: {
1444
+ type: 'string',
1445
+ description: 'Transaction status',
1446
+ enum: ['ACTIVE', 'VOIDED', 'SUPERSEDED'],
1447
+ example: 'ACTIVE'
1448
+ },
1449
+ sourceType: {
1450
+ type: 'string',
1451
+ description:
1452
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1453
+ },
1454
+ sourcePlatform: {
1455
+ type: 'string',
1456
+ description: 'Source platform (e.g., alipay, wechat)',
1457
+ example: 'alipay'
1458
+ },
1459
+ postings: {
1460
+ description: 'Transaction postings',
1461
+ type: 'array',
1462
+ items: {
1463
+ $ref: '#/components/schemas/PostingDetailDto'
1464
+ }
1465
+ },
1466
+ createdAt: {
1467
+ type: 'string',
1468
+ description: 'Created at timestamp',
1469
+ example: '2024-11-28T10:30:00.000Z'
1470
+ },
1471
+ voidedAt: {
1472
+ type: 'string',
1473
+ description: 'Voided at timestamp (if voided)',
1474
+ example: '2024-11-29T15:00:00.000Z'
1475
+ },
1476
+ voidedBy: {
1477
+ type: 'string',
1478
+ description: 'User ID who voided this transaction',
1479
+ example: 'clh1234567890abcdef'
1480
+ },
1481
+ correctionReason: {
1482
+ type: 'string',
1483
+ description: 'Correction reason (if voided or superseded)',
1484
+ example: 'Duplicate entry'
1485
+ },
1486
+ supersededBy: {
1487
+ type: 'string',
1488
+ description:
1489
+ 'ID of the transaction that supersedes this one (set when status=SUPERSEDED)',
1490
+ example: 'clh1234567890abcdef'
1491
+ },
1492
+ originalTxn: {
1493
+ type: 'string',
1494
+ description:
1495
+ 'ID of the transaction this one corrected/replaced (back-link on the replacement)',
1496
+ example: 'clh1234567890abcdef'
1497
+ },
1498
+ viewpointAmount: {
1499
+ type: 'string',
1500
+ description:
1501
+ 'Row amount under the request viewpoint (ADR-0126). Category viewpoint (category + flow): per-leg sign-normalized sum over the category account set (Income-root legs negated, Expenses-root identity) — positive under normal booking but NOT clamped (explicit negative expense legs and net-flip refund months stay negative). No viewpoint (plain list / search, no accountId): wallet money-flow net = raw-sign sum over cost-less Assets/Liabilities legs (income positive, expenses negative, transfers net ~0); color cue is the wallet sign (net < 0 = wealth-decreasing). Status-orthogonal: audit views match too (ADR-0128 amount-as-matching-key). Omitted under the account viewpoint (incl. dual) and for rows with no wallet leg.',
1502
+ example: '10000.00'
1503
+ },
1504
+ viewpointCurrency: {
1505
+ type: 'string',
1506
+ description:
1507
+ 'Currency of viewpointAmount. A row spanning multiple currencies takes the largest-magnitude currency group (known simplification, ADR-0126).',
1508
+ example: 'CNY'
1509
+ }
1510
+ },
1511
+ required: [
1512
+ 'id',
1513
+ 'date',
1514
+ 'narration',
1515
+ 'tags',
1516
+ 'links',
1517
+ 'status',
1518
+ 'postings',
1519
+ 'createdAt'
1520
+ ]
1521
+ } as const;
1522
+
1248
1523
  export const $BalanceByCurrencyDto = {
1249
1524
  type: 'object',
1250
1525
  properties: {
@@ -1290,7 +1565,7 @@ export const $TransactionListSummaryDto = {
1290
1565
  totalAmount: {
1291
1566
  type: 'string',
1292
1567
  description:
1293
- 'Partial converted total in base currency (rated currencies only, raw Beancount sign). 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.',
1568
+ '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.',
1294
1569
  example: '-6000.00'
1295
1570
  },
1296
1571
  currency: {
@@ -1316,6 +1591,26 @@ export const $TransactionListSummaryDto = {
1316
1591
  required: ['totalAmount', 'currency', 'balanceByCurrency']
1317
1592
  } as const;
1318
1593
 
1594
+ export const $TransactionListViewpointDto = {
1595
+ type: 'object',
1596
+ properties: {
1597
+ type: {
1598
+ type: 'string',
1599
+ description:
1600
+ 'Viewpoint type (only category drill-down carries a viewpoint today)',
1601
+ enum: ['category'],
1602
+ example: 'category'
1603
+ },
1604
+ flow: {
1605
+ type: 'string',
1606
+ description: 'Flow root the category account set is restricted to',
1607
+ enum: ['income', 'expense'],
1608
+ example: 'expense'
1609
+ }
1610
+ },
1611
+ required: ['type', 'flow']
1612
+ } as const;
1613
+
1319
1614
  export const $TransactionListResponseDto = {
1320
1615
  type: 'object',
1321
1616
  properties: {
@@ -1323,7 +1618,7 @@ export const $TransactionListResponseDto = {
1323
1618
  description: 'List of transactions',
1324
1619
  type: 'array',
1325
1620
  items: {
1326
- $ref: '#/components/schemas/TransactionDetailDto'
1621
+ $ref: '#/components/schemas/TransactionListItemDto'
1327
1622
  }
1328
1623
  },
1329
1624
  total: {
@@ -1349,6 +1644,15 @@ export const $TransactionListResponseDto = {
1349
1644
  $ref: '#/components/schemas/TransactionListSummaryDto'
1350
1645
  }
1351
1646
  ]
1647
+ },
1648
+ viewpoint: {
1649
+ description:
1650
+ '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).',
1651
+ allOf: [
1652
+ {
1653
+ $ref: '#/components/schemas/TransactionListViewpointDto'
1654
+ }
1655
+ ]
1352
1656
  }
1353
1657
  },
1354
1658
  required: ['data', 'total', 'limit', 'offset']
@@ -1700,13 +2004,17 @@ export const $ReviewStatsDto = {
1700
2004
  type: 'object',
1701
2005
  description: 'Count by type'
1702
2006
  },
2007
+ resolved: {
2008
+ type: 'number',
2009
+ description: 'Current count of reviews in RESOLVED status'
2010
+ },
1703
2011
  oldestPending: {
1704
2012
  format: 'date-time',
1705
2013
  type: 'string',
1706
2014
  description: 'Oldest pending review date'
1707
2015
  }
1708
2016
  },
1709
- required: ['total', 'byType']
2017
+ required: ['total', 'byType', 'resolved']
1710
2018
  } as const;
1711
2019
 
1712
2020
  export const $DecisionOptionDto = {
@@ -2011,29 +2319,67 @@ export const $BatchResolveDto = {
2011
2319
  required: ['reviewIds', 'action']
2012
2320
  } as const;
2013
2321
 
2014
- export const $BatchResolveResultDto = {
2322
+ export const $BatchResolveItemDto = {
2015
2323
  type: 'object',
2016
2324
  properties: {
2017
- successCount: {
2018
- type: 'number',
2019
- description: 'Number of successfully resolved items'
2325
+ reviewId: {
2326
+ type: 'string',
2327
+ description: 'Review item ID'
2020
2328
  },
2021
- failedCount: {
2022
- type: 'number',
2023
- description: 'Number of failed items'
2329
+ success: {
2330
+ type: 'boolean',
2331
+ description: 'Whether this item was resolved successfully'
2024
2332
  },
2025
- results: {
2026
- description: 'Details for each item',
2027
- type: 'array',
2028
- items: {
2029
- type: 'string'
2030
- }
2031
- }
2032
- },
2033
- required: ['successCount', 'failedCount', 'results']
2034
- } as const;
2035
-
2036
- export const $CreatePayeeDto = {
2333
+ resolutionId: {
2334
+ type: 'string',
2335
+ description:
2336
+ 'Resolution ID for undo. Present only on successful items (failed items stay PENDING).'
2337
+ },
2338
+ messageKey: {
2339
+ type: 'string',
2340
+ description:
2341
+ 'i18n message key for the per-item failure reason (e.g., review.payee.error.no_suggestion), mirroring the single-item resolve response. Present only on resolver rejections, not on unexpected errors.'
2342
+ },
2343
+ messageParams: {
2344
+ type: 'object',
2345
+ description:
2346
+ 'Parameters for message interpolation (e.g., { name: "PayeeName" })',
2347
+ additionalProperties: {
2348
+ type: 'string'
2349
+ }
2350
+ },
2351
+ error: {
2352
+ type: 'string',
2353
+ description:
2354
+ 'Diagnostic string: the messageKey on the resolver-rejection path; an i18n key mapped from the exception type on the unexpected-error path (#903, raw exception detail stays in server logs).'
2355
+ }
2356
+ },
2357
+ required: ['reviewId', 'success']
2358
+ } as const;
2359
+
2360
+ export const $BatchResolveResultDto = {
2361
+ type: 'object',
2362
+ properties: {
2363
+ successCount: {
2364
+ type: 'number',
2365
+ description: 'Number of successfully resolved items'
2366
+ },
2367
+ failedCount: {
2368
+ type: 'number',
2369
+ description: 'Number of failed items'
2370
+ },
2371
+ results: {
2372
+ description: 'Details for each item',
2373
+ type: 'array',
2374
+ items: {
2375
+ $ref: '#/components/schemas/BatchResolveItemDto'
2376
+ }
2377
+ }
2378
+ },
2379
+ required: ['successCount', 'failedCount', 'results']
2380
+ } as const;
2381
+
2382
+ export const $CreatePayeeDto = {
2037
2383
  type: 'object',
2038
2384
  properties: {
2039
2385
  payee: {
@@ -2259,10 +2605,10 @@ export const $UpdatePayeeDto = {
2259
2605
  meta: {
2260
2606
  type: 'object',
2261
2607
  description:
2262
- 'Metadata for extended information (location, notes, contact info, etc.). Will merge with existing metadata.',
2608
+ 'Metadata for extended information (location, notes, contact info, etc.)',
2263
2609
  example: {
2264
2610
  location: 'Zhongguancun',
2265
- note: 'Updated note',
2611
+ note: 'Near subway station',
2266
2612
  favorite: true
2267
2613
  }
2268
2614
  },
@@ -2823,568 +3169,660 @@ export const $UpdateCommodityDto = {
2823
3169
  }
2824
3170
  } as const;
2825
3171
 
2826
- export const $CreateBeanPriceDto = {
3172
+ export const $CurrencyBalanceDto = {
2827
3173
  type: 'object',
2828
3174
  properties: {
2829
3175
  currency: {
2830
3176
  type: 'string',
2831
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2832
- example: 'USD'
2833
- },
2834
- quoteCurrency: {
2835
- type: 'string',
2836
- description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3177
+ description: 'ISO 4217 currency code',
2837
3178
  example: 'CNY'
2838
3179
  },
2839
- amount: {
2840
- type: 'number',
2841
- description:
2842
- 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
2843
- example: 175.5,
2844
- minimum: 0
2845
- },
2846
- date: {
3180
+ balance: {
2847
3181
  type: 'string',
2848
- description: 'Price date (ISO 8601 format)',
2849
- example: '2024-11-05'
2850
- },
2851
- metadata: {
2852
- type: 'object',
2853
- description:
2854
- 'Metadata (validated by Zod schema, max field lengths enforced)',
2855
- example: {
2856
- source: 'MANUAL',
2857
- note: 'Bank valuation report',
2858
- confidence: 0.95
2859
- }
3182
+ description: 'Balance amount',
3183
+ example: '500000.00'
2860
3184
  }
2861
3185
  },
2862
- required: ['currency', 'quoteCurrency', 'amount', 'date']
3186
+ required: ['currency', 'balance']
2863
3187
  } as const;
2864
3188
 
2865
- export const $PriceResponseDto = {
3189
+ export const $TimeSeriesPointDto = {
2866
3190
  type: 'object',
2867
3191
  properties: {
2868
- id: {
3192
+ date: {
2869
3193
  type: 'string',
2870
- description: 'Unique identifier',
2871
- example: 'uuid-123-456'
3194
+ description: 'Date in YYYY-MM-DD format',
3195
+ example: '2024-06-15'
2872
3196
  },
2873
- userId: {
3197
+ value: {
2874
3198
  type: 'string',
2875
- description: 'User ID (owner of the price)',
2876
- example: 'user-123'
3199
+ description: 'Value at this date (in base currency)',
3200
+ example: '500000.00'
2877
3201
  },
2878
- currency: {
3202
+ change: {
2879
3203
  type: 'string',
2880
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2881
- example: 'BTC'
3204
+ description: 'Change from previous point',
3205
+ example: '5000.00'
2882
3206
  },
2883
- quoteCurrency: {
3207
+ assets: {
2884
3208
  type: 'string',
2885
- description: 'Quote currency (pricing currency, e.g., USD, CNY)',
2886
- example: 'USD'
2887
- },
2888
- amount: {
2889
- type: 'number',
2890
- description:
2891
- 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
2892
- example: 50000
3209
+ description: 'Total assets at this date (in base currency)',
3210
+ example: '494338.00'
2893
3211
  },
2894
- date: {
3212
+ liabilities: {
2895
3213
  type: 'string',
2896
- description:
2897
- 'Price date (ISO 8601 format). Represents the date this price was valid.',
2898
- example: '2024-01-01',
2899
- format: 'date'
3214
+ description: 'Total liabilities at this date (in base currency)',
3215
+ example: '310098.00'
2900
3216
  },
2901
- meta: {
2902
- type: 'object',
2903
- description:
2904
- 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
2905
- example: {
2906
- source: 'MANUAL',
2907
- note: 'User-defined price',
2908
- confidence: 1
3217
+ byCurrency: {
3218
+ description: 'Multi-currency breakdown for this point',
3219
+ type: 'array',
3220
+ items: {
3221
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2909
3222
  }
3223
+ }
3224
+ },
3225
+ required: ['date', 'value']
3226
+ } as const;
3227
+
3228
+ export const $TrendSummaryDto = {
3229
+ type: 'object',
3230
+ properties: {
3231
+ startValue: {
3232
+ type: 'string',
3233
+ description: 'Value at start of period',
3234
+ example: '450000.00'
2910
3235
  },
2911
- createdAt: {
2912
- format: 'date-time',
3236
+ endValue: {
2913
3237
  type: 'string',
2914
- description: 'Creation timestamp',
2915
- example: '2024-11-03T10:00:00Z'
3238
+ description: 'Value at end of period',
3239
+ example: '500000.00'
2916
3240
  },
2917
- updatedAt: {
2918
- format: 'date-time',
3241
+ totalChange: {
2919
3242
  type: 'string',
2920
- description: 'Last update timestamp',
2921
- example: '2024-11-03T10:00:00Z'
3243
+ description: 'Total change over period',
3244
+ example: '50000.00'
3245
+ },
3246
+ totalChangePercentage: {
3247
+ type: 'string',
3248
+ description: 'Total change percentage',
3249
+ example: '+11.11%'
2922
3250
  }
2923
3251
  },
2924
- required: [
2925
- 'id',
2926
- 'userId',
2927
- 'currency',
2928
- 'quoteCurrency',
2929
- 'amount',
2930
- 'date',
2931
- 'meta',
2932
- 'createdAt',
2933
- 'updatedAt'
2934
- ]
3252
+ required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
2935
3253
  } as const;
2936
3254
 
2937
- export const $PriceListResponseDto = {
3255
+ export const $MultiCurrencyPointDto = {
2938
3256
  type: 'object',
2939
3257
  properties: {
2940
- items: {
2941
- description: 'List of prices',
3258
+ date: {
3259
+ type: 'string',
3260
+ description: 'Date in YYYY-MM-DD format',
3261
+ example: '2024-06-15'
3262
+ },
3263
+ byCurrency: {
3264
+ description: 'Balances by currency',
2942
3265
  type: 'array',
2943
3266
  items: {
2944
- $ref: '#/components/schemas/PriceResponseDto'
3267
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2945
3268
  }
2946
- },
2947
- total: {
2948
- type: 'number',
2949
- description: 'Total number of prices',
2950
- example: 42
2951
3269
  }
2952
3270
  },
2953
- required: ['items', 'total']
3271
+ required: ['date', 'byCurrency']
2954
3272
  } as const;
2955
3273
 
2956
- export const $UpdateBeanPriceDto = {
3274
+ export const $PortfolioTrendsResponseDto = {
2957
3275
  type: 'object',
2958
3276
  properties: {
2959
- currency: {
2960
- type: 'string',
2961
- description: 'Currency being priced'
3277
+ series: {
3278
+ description: 'Time series data points',
3279
+ type: 'array',
3280
+ items: {
3281
+ $ref: '#/components/schemas/TimeSeriesPointDto'
3282
+ }
2962
3283
  },
2963
- quoteCurrency: {
3284
+ summary: {
3285
+ description: 'Period summary',
3286
+ allOf: [
3287
+ {
3288
+ $ref: '#/components/schemas/TrendSummaryDto'
3289
+ }
3290
+ ]
3291
+ },
3292
+ period: {
2964
3293
  type: 'string',
2965
- description: 'Quote currency (pricing currency)'
3294
+ description: 'Period requested',
3295
+ example: '6m'
2966
3296
  },
2967
- amount: {
2968
- type: 'number',
2969
- description: 'Price amount (MUST be >= 0 per Beancount spec)',
2970
- minimum: 0
3297
+ granularity: {
3298
+ type: 'string',
3299
+ description: 'Data granularity',
3300
+ example: 'month'
2971
3301
  },
2972
- date: {
3302
+ currency: {
2973
3303
  type: 'string',
2974
- description: 'Price date (ISO 8601 format)'
3304
+ description: 'Base currency for converted values',
3305
+ example: 'CNY'
2975
3306
  },
2976
- metadata: {
2977
- type: 'object',
2978
- description: 'Metadata'
3307
+ byCurrency: {
3308
+ description:
3309
+ 'Multi-currency time series (each point has currency breakdown)',
3310
+ type: 'array',
3311
+ items: {
3312
+ $ref: '#/components/schemas/MultiCurrencyPointDto'
3313
+ }
3314
+ },
3315
+ warnings: {
3316
+ description: 'Exchange rate warnings',
3317
+ type: 'array',
3318
+ items: {
3319
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3320
+ }
2979
3321
  }
2980
- }
3322
+ },
3323
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
2981
3324
  } as const;
2982
3325
 
2983
- export const $CreateRecurringRuleDto = {
3326
+ export const $CashFlowPointDto = {
2984
3327
  type: 'object',
2985
3328
  properties: {
2986
- name: {
3329
+ month: {
2987
3330
  type: 'string',
2988
- description: 'Rule name (unique per user)',
2989
- maxLength: 100
3331
+ description: 'Month key (YYYY-MM)',
3332
+ example: '2024-03'
2990
3333
  },
2991
- icon: {
3334
+ income: {
2992
3335
  type: 'string',
2993
- description: 'Icon emoji',
2994
- maxLength: 10
3336
+ description: 'Income in base currency (absolute, converted)',
3337
+ example: '10000.00'
2995
3338
  },
2996
- frequency: {
3339
+ expense: {
2997
3340
  type: 'string',
2998
- description: 'Recurring frequency',
2999
- enum: [
3000
- 'WEEKLY',
3001
- 'BIWEEKLY',
3002
- 'MONTHLY',
3003
- 'BIMONTHLY',
3004
- 'QUARTERLY',
3005
- 'YEARLY',
3006
- 'CUSTOM'
3007
- ]
3008
- },
3009
- expectedAmount: {
3010
- type: 'number',
3011
- description: 'Expected amount (positive number)',
3012
- minimum: 0
3341
+ description: 'Expense in base currency (absolute, converted)',
3342
+ example: '5000.00'
3013
3343
  },
3014
- expectedDay: {
3015
- type: 'number',
3016
- description: 'Expected day of month (1-31)',
3017
- minimum: 1,
3018
- maximum: 31
3344
+ netSavings: {
3345
+ type: 'string',
3346
+ description: 'netSavings = income − expense (savings positive)',
3347
+ example: '5000.00'
3348
+ }
3349
+ },
3350
+ required: ['month', 'income', 'expense', 'netSavings']
3351
+ } as const;
3352
+
3353
+ export const $CashFlowTrendSummaryDto = {
3354
+ type: 'object',
3355
+ properties: {
3356
+ totalIncome: {
3357
+ type: 'string',
3358
+ description: 'Total income across the period',
3359
+ example: '60000.00'
3019
3360
  },
3020
- customIntervalDays: {
3021
- type: 'number',
3022
- description: 'Custom interval in days (required for CUSTOM frequency)',
3023
- minimum: 1
3361
+ totalExpense: {
3362
+ type: 'string',
3363
+ description: 'Total expense across the period',
3364
+ example: '30000.00'
3024
3365
  },
3025
- currency: {
3366
+ totalNetSavings: {
3026
3367
  type: 'string',
3027
- description: 'Currency code',
3028
- default: 'CNY',
3029
- maxLength: 10
3368
+ description: 'income − expense across the period',
3369
+ example: '30000.00'
3030
3370
  },
3031
- matchPayeePattern: {
3371
+ averageMonthlyNetSavings: {
3032
3372
  type: 'string',
3033
- description: 'Payee matching pattern (supports wildcards)',
3034
- maxLength: 200
3373
+ description:
3374
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3375
+ example: '5000.00'
3376
+ }
3377
+ },
3378
+ required: [
3379
+ 'totalIncome',
3380
+ 'totalExpense',
3381
+ 'totalNetSavings',
3382
+ 'averageMonthlyNetSavings'
3383
+ ]
3384
+ } as const;
3385
+
3386
+ export const $CashFlowTrendsResponseDto = {
3387
+ type: 'object',
3388
+ properties: {
3389
+ series: {
3390
+ description:
3391
+ 'Monthly cash-flow series (fixed N-month window, zero-filled)',
3392
+ type: 'array',
3393
+ items: {
3394
+ $ref: '#/components/schemas/CashFlowPointDto'
3395
+ }
3035
3396
  },
3036
- matchAmountTolerance: {
3037
- type: 'number',
3038
- description: 'Amount tolerance percentage (0-1)',
3039
- default: 0.075,
3040
- minimum: 0,
3041
- maximum: 1
3397
+ summary: {
3398
+ description: 'Period totals',
3399
+ allOf: [
3400
+ {
3401
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
3402
+ }
3403
+ ]
3042
3404
  },
3043
- defaultExpenseAccount: {
3405
+ period: {
3044
3406
  type: 'string',
3045
- description: 'Default expense account for auto-create',
3046
- maxLength: 200
3407
+ description: 'Period requested',
3408
+ example: '6m'
3047
3409
  },
3048
- defaultPaymentAccount: {
3410
+ granularity: {
3049
3411
  type: 'string',
3050
- description: 'Default payment account for auto-create',
3051
- maxLength: 200
3412
+ description: 'Data granularity (v1 returns month buckets)',
3413
+ example: 'month'
3052
3414
  },
3053
- defaultPayee: {
3415
+ currency: {
3054
3416
  type: 'string',
3055
- description: 'Default payee for auto-create',
3056
- maxLength: 200
3417
+ description: 'Base currency for converted values',
3418
+ example: 'CNY'
3057
3419
  },
3058
- autoCreate: {
3059
- type: 'boolean',
3060
- description: 'Auto-create transaction when expected date arrives',
3061
- default: false
3420
+ warnings: {
3421
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
3422
+ type: 'array',
3423
+ items: {
3424
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3425
+ }
3426
+ }
3427
+ },
3428
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3429
+ } as const;
3430
+
3431
+ export const $GenerateSnapshotBody = {
3432
+ type: 'object',
3433
+ properties: {}
3434
+ } as const;
3435
+
3436
+ export const $GenerateSnapshotResponse = {
3437
+ type: 'object',
3438
+ properties: {}
3439
+ } as const;
3440
+
3441
+ export const $BackfillSnapshotsBody = {
3442
+ type: 'object',
3443
+ properties: {}
3444
+ } as const;
3445
+
3446
+ export const $BackfillSnapshotsResponse = {
3447
+ type: 'object',
3448
+ properties: {}
3449
+ } as const;
3450
+
3451
+ export const $CreateBeanPriceDto = {
3452
+ type: 'object',
3453
+ properties: {
3454
+ currency: {
3455
+ type: 'string',
3456
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3457
+ example: 'USD'
3062
3458
  },
3063
- startDate: {
3459
+ quoteCurrency: {
3064
3460
  type: 'string',
3065
- description: 'Rule start date (ISO format)'
3461
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3462
+ example: 'CNY'
3066
3463
  },
3067
- endDate: {
3464
+ amount: {
3465
+ type: 'number',
3466
+ description:
3467
+ 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
3468
+ example: 175.5,
3469
+ minimum: 0
3470
+ },
3471
+ date: {
3068
3472
  type: 'string',
3069
- description: 'Rule end date (ISO format)'
3473
+ description: 'Price date (ISO 8601 format)',
3474
+ example: '2024-11-05'
3475
+ },
3476
+ metadata: {
3477
+ type: 'object',
3478
+ description:
3479
+ 'Metadata (validated by Zod schema, max field lengths enforced)',
3480
+ example: {
3481
+ source: 'MANUAL',
3482
+ note: 'Bank valuation report',
3483
+ confidence: 0.95
3484
+ }
3070
3485
  }
3071
3486
  },
3072
- required: [
3073
- 'name',
3074
- 'frequency',
3075
- 'expectedAmount',
3076
- 'currency',
3077
- 'matchAmountTolerance',
3078
- 'autoCreate'
3079
- ]
3487
+ required: ['currency', 'quoteCurrency', 'amount', 'date']
3080
3488
  } as const;
3081
3489
 
3082
- export const $RecurringRuleResponseDto = {
3490
+ export const $PriceResponseDto = {
3083
3491
  type: 'object',
3084
3492
  properties: {
3085
3493
  id: {
3086
3494
  type: 'string',
3087
- description: 'Rule ID'
3495
+ description: 'Unique identifier',
3496
+ example: 'uuid-123-456'
3088
3497
  },
3089
3498
  userId: {
3090
3499
  type: 'string',
3091
- description: 'User ID'
3500
+ description: 'User ID (owner of the price)',
3501
+ example: 'user-123'
3092
3502
  },
3093
- name: {
3503
+ currency: {
3094
3504
  type: 'string',
3095
- description: 'Rule name'
3096
- },
3097
- icon: {
3098
- type: 'object',
3099
- description: 'Icon emoji'
3505
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3506
+ example: 'BTC'
3100
3507
  },
3101
- frequency: {
3508
+ quoteCurrency: {
3102
3509
  type: 'string',
3103
- description: 'Recurring frequency'
3510
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
3511
+ example: 'USD'
3104
3512
  },
3105
- expectedAmount: {
3513
+ amount: {
3106
3514
  type: 'number',
3107
- description: 'Expected amount'
3515
+ description:
3516
+ 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
3517
+ example: 50000
3108
3518
  },
3109
- expectedDay: {
3110
- type: 'object',
3111
- description: 'Expected day of month'
3519
+ date: {
3520
+ type: 'string',
3521
+ description:
3522
+ 'Price date (ISO 8601 format). Represents the date this price was valid.',
3523
+ example: '2024-01-01',
3524
+ format: 'date'
3112
3525
  },
3113
- customIntervalDays: {
3526
+ meta: {
3114
3527
  type: 'object',
3115
- description: 'Custom interval in days'
3528
+ description:
3529
+ 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
3530
+ example: {
3531
+ source: 'MANUAL',
3532
+ note: 'User-defined price',
3533
+ confidence: 1
3534
+ }
3116
3535
  },
3117
- currency: {
3536
+ createdAt: {
3537
+ format: 'date-time',
3118
3538
  type: 'string',
3119
- description: 'Currency code'
3120
- },
3121
- matchPayeePattern: {
3122
- type: 'object',
3123
- description: 'Payee matching pattern'
3124
- },
3125
- matchAmountTolerance: {
3126
- type: 'number',
3127
- description: 'Amount tolerance percentage'
3128
- },
3129
- defaultExpenseAccount: {
3130
- type: 'object',
3131
- description: 'Default expense account'
3132
- },
3133
- defaultPaymentAccount: {
3134
- type: 'object',
3135
- description: 'Default payment account'
3136
- },
3137
- defaultPayee: {
3138
- type: 'object',
3139
- description: 'Default payee'
3140
- },
3141
- isActive: {
3142
- type: 'boolean',
3143
- description: 'Whether rule is active'
3144
- },
3145
- startDate: {
3146
- type: 'string',
3147
- description: 'Rule start date (YYYY-MM-DD)'
3148
- },
3149
- endDate: {
3150
- type: 'object',
3151
- description: 'Rule end date (YYYY-MM-DD)'
3152
- },
3153
- autoCreate: {
3154
- type: 'boolean',
3155
- description: 'Auto-create transaction on expected date'
3156
- },
3157
- lastOccurrence: {
3158
- type: 'object',
3159
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3160
- },
3161
- totalCount: {
3162
- type: 'number',
3163
- description: 'Total matched transactions count'
3164
- },
3165
- createdAt: {
3166
- format: 'date-time',
3167
- type: 'string',
3168
- description: 'Created at timestamp'
3539
+ description: 'Creation timestamp',
3540
+ example: '2024-11-03T10:00:00Z'
3169
3541
  },
3170
3542
  updatedAt: {
3171
3543
  format: 'date-time',
3172
3544
  type: 'string',
3173
- description: 'Updated at timestamp'
3545
+ description: 'Last update timestamp',
3546
+ example: '2024-11-03T10:00:00Z'
3174
3547
  }
3175
3548
  },
3176
3549
  required: [
3177
3550
  'id',
3178
3551
  'userId',
3179
- 'name',
3180
- 'frequency',
3181
- 'expectedAmount',
3182
3552
  'currency',
3183
- 'matchAmountTolerance',
3184
- 'isActive',
3185
- 'startDate',
3186
- 'autoCreate',
3187
- 'totalCount',
3553
+ 'quoteCurrency',
3554
+ 'amount',
3555
+ 'date',
3556
+ 'meta',
3188
3557
  'createdAt',
3189
3558
  'updatedAt'
3190
3559
  ]
3191
3560
  } as const;
3192
3561
 
3193
- export const $CreateRuleFromTransactionDto = {
3562
+ export const $PriceListResponseDto = {
3194
3563
  type: 'object',
3195
3564
  properties: {
3196
- frequency: {
3565
+ items: {
3566
+ description: 'List of prices',
3567
+ type: 'array',
3568
+ items: {
3569
+ $ref: '#/components/schemas/PriceResponseDto'
3570
+ }
3571
+ },
3572
+ total: {
3573
+ type: 'number',
3574
+ description: 'Total number of prices',
3575
+ example: 42
3576
+ }
3577
+ },
3578
+ required: ['items', 'total']
3579
+ } as const;
3580
+
3581
+ export const $UpdateBeanPriceDto = {
3582
+ type: 'object',
3583
+ properties: {
3584
+ currency: {
3197
3585
  type: 'string',
3198
- description: 'Recurring frequency',
3199
- enum: [
3200
- 'WEEKLY',
3201
- 'BIWEEKLY',
3202
- 'MONTHLY',
3203
- 'BIMONTHLY',
3204
- 'QUARTERLY',
3205
- 'YEARLY',
3206
- 'CUSTOM'
3207
- ],
3208
- example: 'MONTHLY'
3586
+ description: 'Currency being priced'
3209
3587
  },
3210
- name: {
3588
+ quoteCurrency: {
3211
3589
  type: 'string',
3212
- description: 'Optional name override (default: transaction payee)',
3213
- maxLength: 100
3590
+ description: 'Quote currency (pricing currency)'
3214
3591
  },
3215
- icon: {
3592
+ amount: {
3593
+ type: 'number',
3594
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
3595
+ minimum: 0
3596
+ },
3597
+ date: {
3216
3598
  type: 'string',
3217
- description: 'Optional icon emoji',
3218
- maxLength: 10
3599
+ description: 'Price date (ISO 8601 format)'
3600
+ },
3601
+ metadata: {
3602
+ type: 'object',
3603
+ description: 'Metadata'
3604
+ }
3605
+ }
3606
+ } as const;
3607
+
3608
+ export const $DeleteOwnUserDto = {
3609
+ type: 'object',
3610
+ properties: {
3611
+ accessToken: {
3612
+ type: 'string',
3613
+ description: 'Access token for user verification',
3614
+ example: 'abc123xyz'
3219
3615
  }
3220
3616
  },
3221
- required: ['frequency']
3617
+ required: ['accessToken']
3222
3618
  } as const;
3223
3619
 
3224
- export const $RecurringRuleWithStatsResponseDto = {
3620
+ export const $UserSettingsResponseDto = {
3225
3621
  type: 'object',
3226
3622
  properties: {
3227
- id: {
3623
+ baseCurrency: {
3228
3624
  type: 'string',
3229
- description: 'Rule ID'
3230
- },
3231
- userId: {
3625
+ description:
3626
+ 'Stored base currency choice (ISO 4217) for net-worth/report aggregation. null = user never chose; aggregates fall back to the region default at display time (#713).',
3627
+ example: 'USD',
3628
+ nullable: true
3629
+ }
3630
+ },
3631
+ required: ['baseCurrency']
3632
+ } as const;
3633
+
3634
+ export const $UserResponseDto = {
3635
+ type: 'object',
3636
+ properties: {
3637
+ id: {
3232
3638
  type: 'string',
3233
3639
  description: 'User ID'
3234
3640
  },
3235
- name: {
3641
+ role: {
3236
3642
  type: 'string',
3237
- description: 'Rule name'
3643
+ description: 'Assigned user role'
3238
3644
  },
3239
- icon: {
3240
- type: 'object',
3241
- description: 'Icon emoji'
3645
+ permissions: {
3646
+ description: 'Permission strings',
3647
+ type: 'array',
3648
+ items: {
3649
+ type: 'string'
3650
+ }
3242
3651
  },
3243
- frequency: {
3652
+ settings: {
3653
+ description: 'User settings',
3654
+ allOf: [
3655
+ {
3656
+ $ref: '#/components/schemas/UserSettingsResponseDto'
3657
+ }
3658
+ ]
3659
+ }
3660
+ },
3661
+ required: ['id', 'role', 'permissions', 'settings']
3662
+ } as const;
3663
+
3664
+ export const $SignupDto = {
3665
+ type: 'object',
3666
+ properties: {
3667
+ turnstileToken: {
3244
3668
  type: 'string',
3245
- description: 'Recurring frequency'
3246
- },
3247
- expectedAmount: {
3248
- type: 'number',
3249
- description: 'Expected amount'
3250
- },
3251
- expectedDay: {
3252
- type: 'object',
3253
- description: 'Expected day of month'
3254
- },
3255
- customIntervalDays: {
3256
- type: 'object',
3257
- description: 'Custom interval in days'
3669
+ description:
3670
+ 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
3671
+ example: '0.abc123def456...'
3672
+ }
3673
+ }
3674
+ } as const;
3675
+
3676
+ export const $SignupResponseDto = {
3677
+ type: 'object',
3678
+ properties: {
3679
+ authToken: {
3680
+ type: 'string',
3681
+ description: 'JWT auth token',
3682
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
3258
3683
  },
3259
- currency: {
3684
+ accessToken: {
3260
3685
  type: 'string',
3261
- description: 'Currency code'
3686
+ description: 'Auto-generated access token'
3262
3687
  },
3263
- matchPayeePattern: {
3264
- type: 'object',
3265
- description: 'Payee matching pattern'
3688
+ role: {
3689
+ type: 'string',
3690
+ description: 'Assigned user role',
3691
+ enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
3692
+ }
3693
+ },
3694
+ required: ['authToken', 'accessToken', 'role']
3695
+ } as const;
3696
+
3697
+ export const $UpdateUserSettingDto = {
3698
+ type: 'object',
3699
+ properties: {
3700
+ secId: {
3701
+ type: 'number',
3702
+ description: 'Security ID'
3266
3703
  },
3267
- matchAmountTolerance: {
3704
+ annualInterestRate: {
3268
3705
  type: 'number',
3269
- description: 'Amount tolerance percentage'
3706
+ description: 'Annual interest rate',
3707
+ example: 0.05
3270
3708
  },
3271
- defaultExpenseAccount: {
3272
- type: 'object',
3273
- description: 'Default expense account'
3709
+ currency: {
3710
+ type: 'string',
3711
+ description: 'Currency code',
3712
+ example: 'USD'
3274
3713
  },
3275
- defaultPaymentAccount: {
3276
- type: 'object',
3277
- description: 'Default payment account'
3714
+ baseCurrency: {
3715
+ type: 'string',
3716
+ description: 'Base currency code',
3717
+ example: 'USD'
3278
3718
  },
3279
- defaultPayee: {
3280
- type: 'object',
3281
- description: 'Default payee'
3719
+ benchmark: {
3720
+ type: 'string',
3721
+ description: 'Benchmark symbol',
3722
+ example: 'SPY'
3282
3723
  },
3283
- isActive: {
3284
- type: 'boolean',
3285
- description: 'Whether rule is active'
3724
+ colorScheme: {
3725
+ type: 'string',
3726
+ description: 'Color scheme',
3727
+ enum: ['DARK', 'LIGHT']
3286
3728
  },
3287
- startDate: {
3729
+ dateRange: {
3288
3730
  type: 'string',
3289
- description: 'Rule start date (YYYY-MM-DD)'
3731
+ description: 'Date range filter',
3732
+ example: '1y'
3290
3733
  },
3291
- endDate: {
3292
- type: 'object',
3293
- description: 'Rule end date (YYYY-MM-DD)'
3294
- },
3295
- autoCreate: {
3296
- type: 'boolean',
3297
- description: 'Auto-create transaction on expected date'
3734
+ emergencyFund: {
3735
+ type: 'number',
3736
+ description: 'Emergency fund amount',
3737
+ example: 10000
3298
3738
  },
3299
- lastOccurrence: {
3300
- type: 'object',
3301
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3739
+ 'filters.accounts': {
3740
+ description: 'Account filter IDs',
3741
+ type: 'array',
3742
+ items: {
3743
+ type: 'string'
3744
+ }
3302
3745
  },
3303
- totalCount: {
3304
- type: 'number',
3305
- description: 'Total matched transactions count'
3746
+ 'filters.assetClasses': {
3747
+ description: 'Asset class filters',
3748
+ type: 'array',
3749
+ items: {
3750
+ type: 'string'
3751
+ }
3306
3752
  },
3307
- createdAt: {
3308
- format: 'date-time',
3753
+ 'filters.dataSource': {
3309
3754
  type: 'string',
3310
- description: 'Created at timestamp'
3755
+ description: 'Data source filter'
3311
3756
  },
3312
- updatedAt: {
3313
- format: 'date-time',
3757
+ 'filters.symbol': {
3314
3758
  type: 'string',
3315
- description: 'Updated at timestamp'
3759
+ description: 'Symbol filter'
3316
3760
  },
3317
- pendingCount: {
3318
- type: 'number',
3319
- description: 'Number of pending expected transactions'
3761
+ 'filters.tags': {
3762
+ description: 'Tag filters',
3763
+ type: 'array',
3764
+ items: {
3765
+ type: 'string'
3766
+ }
3320
3767
  },
3321
- overdueCount: {
3322
- type: 'number',
3323
- description: 'Number of overdue expected transactions'
3768
+ isExperimentalFeatures: {
3769
+ type: 'boolean',
3770
+ description: 'Enable experimental features'
3324
3771
  },
3325
- nextExpectedDate: {
3326
- type: 'object',
3327
- description: 'Next expected date (YYYY-MM-DD)'
3772
+ isRestrictedView: {
3773
+ type: 'boolean',
3774
+ description: 'Enable restricted view mode'
3328
3775
  },
3329
- totalAmount: {
3330
- type: 'number',
3331
- description: 'Total amount of all matched transactions'
3776
+ language: {
3777
+ type: 'string',
3778
+ description: 'Language code',
3779
+ example: 'en'
3332
3780
  },
3333
- averageAmount: {
3334
- type: 'number',
3335
- description: 'Average amount per transaction'
3781
+ locale: {
3782
+ type: 'string',
3783
+ description: 'Locale code',
3784
+ example: 'en-US'
3336
3785
  },
3337
- transactionCount: {
3786
+ projectedTotalAmount: {
3338
3787
  type: 'number',
3339
- description: 'Number of matched transactions'
3340
- },
3341
- firstDate: {
3342
- type: 'object',
3343
- description: 'First matched transaction date (YYYY-MM-DD)'
3788
+ description: 'Projected total amount',
3789
+ example: 1000000
3344
3790
  },
3345
- lastDate: {
3346
- type: 'object',
3347
- description: 'Last matched transaction date (YYYY-MM-DD)'
3791
+ retirementDate: {
3792
+ type: 'string',
3793
+ description: 'Retirement date in ISO 8601 format',
3794
+ example: '2050-01-01'
3348
3795
  },
3349
- variance: {
3796
+ savingsRate: {
3350
3797
  type: 'number',
3351
- description: 'Amount variance (standard deviation squared)'
3798
+ description: 'Savings rate percentage',
3799
+ example: 0.2
3352
3800
  },
3353
- upcomingCount: {
3354
- type: 'number',
3355
- description: 'Number of upcoming expected transactions'
3801
+ viewMode: {
3802
+ type: 'string',
3803
+ description: 'View mode',
3804
+ enum: ['DEFAULT', 'ZEN']
3805
+ }
3806
+ }
3807
+ } as const;
3808
+
3809
+ export const $UpdatePropertyDto = {
3810
+ type: 'object',
3811
+ properties: {
3812
+ value: {
3813
+ type: 'string',
3814
+ description: 'Property value'
3356
3815
  }
3357
3816
  },
3358
- required: [
3359
- 'id',
3360
- 'userId',
3361
- 'name',
3362
- 'frequency',
3363
- 'expectedAmount',
3364
- 'currency',
3365
- 'matchAmountTolerance',
3366
- 'isActive',
3367
- 'startDate',
3368
- 'autoCreate',
3369
- 'totalCount',
3370
- 'createdAt',
3371
- 'updatedAt',
3372
- 'pendingCount',
3373
- 'overdueCount',
3374
- 'totalAmount',
3375
- 'averageAmount',
3376
- 'transactionCount',
3377
- 'variance',
3378
- 'upcomingCount'
3379
- ]
3817
+ required: ['value']
3380
3818
  } as const;
3381
3819
 
3382
- export const $UpdateRecurringRuleDto = {
3820
+ export const $CreateRecurringRuleDto = {
3383
3821
  type: 'object',
3384
3822
  properties: {
3385
3823
  name: {
3386
3824
  type: 'string',
3387
- description: 'Rule name',
3825
+ description: 'Rule name (unique per user)',
3388
3826
  maxLength: 100
3389
3827
  },
3390
3828
  icon: {
@@ -3407,7 +3845,7 @@ export const $UpdateRecurringRuleDto = {
3407
3845
  },
3408
3846
  expectedAmount: {
3409
3847
  type: 'number',
3410
- description: 'Expected amount',
3848
+ description: 'Expected amount (positive number)',
3411
3849
  minimum: 0
3412
3850
  },
3413
3851
  expectedDay: {
@@ -3418,7 +3856,7 @@ export const $UpdateRecurringRuleDto = {
3418
3856
  },
3419
3857
  customIntervalDays: {
3420
3858
  type: 'number',
3421
- description: 'Custom interval in days',
3859
+ description: 'Custom interval in days (required for CUSTOM frequency)',
3422
3860
  minimum: 1
3423
3861
  },
3424
3862
  currency: {
@@ -3428,118 +3866,136 @@ export const $UpdateRecurringRuleDto = {
3428
3866
  },
3429
3867
  matchPayeePattern: {
3430
3868
  type: 'string',
3431
- description: 'Payee matching pattern',
3869
+ description: 'Payee matching pattern (supports wildcards)',
3432
3870
  maxLength: 200
3433
3871
  },
3434
3872
  matchAmountTolerance: {
3435
3873
  type: 'number',
3436
3874
  description: 'Amount tolerance percentage (0-1)',
3875
+ default: 0.075,
3437
3876
  minimum: 0,
3438
3877
  maximum: 1
3439
3878
  },
3440
3879
  defaultExpenseAccount: {
3441
3880
  type: 'string',
3442
- description: 'Default expense account',
3881
+ description: 'Default expense account for auto-create',
3443
3882
  maxLength: 200
3444
3883
  },
3445
3884
  defaultPaymentAccount: {
3446
3885
  type: 'string',
3447
- description: 'Default payment account',
3886
+ description: 'Default payment account for auto-create',
3448
3887
  maxLength: 200
3449
3888
  },
3450
3889
  defaultPayee: {
3451
3890
  type: 'string',
3452
- description: 'Default payee',
3891
+ description: 'Default payee for auto-create',
3453
3892
  maxLength: 200
3454
3893
  },
3455
3894
  autoCreate: {
3456
3895
  type: 'boolean',
3457
- description: 'Auto-create transaction'
3896
+ description: 'Auto-create transaction when expected date arrives',
3897
+ default: false
3458
3898
  },
3459
- isActive: {
3460
- type: 'boolean',
3461
- description: 'Rule active status'
3899
+ startDate: {
3900
+ type: 'string',
3901
+ description: 'Rule start date (ISO format)'
3462
3902
  },
3463
3903
  endDate: {
3464
3904
  type: 'string',
3465
3905
  description: 'Rule end date (ISO format)'
3466
3906
  }
3467
- }
3907
+ },
3908
+ required: [
3909
+ 'name',
3910
+ 'frequency',
3911
+ 'expectedAmount',
3912
+ 'matchAmountTolerance',
3913
+ 'autoCreate'
3914
+ ]
3468
3915
  } as const;
3469
3916
 
3470
- export const $ExpectedTransactionRuleDto = {
3917
+ export const $RecurringRuleResponseDto = {
3471
3918
  type: 'object',
3472
3919
  properties: {
3920
+ id: {
3921
+ type: 'string',
3922
+ description: 'Rule ID'
3923
+ },
3924
+ userId: {
3925
+ type: 'string',
3926
+ description: 'User ID'
3927
+ },
3473
3928
  name: {
3474
3929
  type: 'string',
3475
3930
  description: 'Rule name'
3476
3931
  },
3477
3932
  icon: {
3478
- type: 'object',
3479
- description: 'Rule icon'
3933
+ type: 'string',
3934
+ description: 'Icon emoji'
3480
3935
  },
3481
3936
  frequency: {
3482
3937
  type: 'string',
3483
- description: 'Rule frequency'
3938
+ description: 'Recurring frequency'
3939
+ },
3940
+ expectedAmount: {
3941
+ type: 'number',
3942
+ description: 'Expected amount'
3943
+ },
3944
+ expectedDay: {
3945
+ type: 'number',
3946
+ description: 'Expected day of month'
3947
+ },
3948
+ customIntervalDays: {
3949
+ type: 'number',
3950
+ description: 'Custom interval in days'
3484
3951
  },
3485
3952
  currency: {
3486
3953
  type: 'string',
3487
3954
  description: 'Currency code'
3488
- }
3489
- },
3490
- required: ['name', 'frequency', 'currency']
3491
- } as const;
3492
-
3493
- export const $ExpectedTransactionResponseDto = {
3494
- type: 'object',
3495
- properties: {
3496
- id: {
3497
- type: 'string',
3498
- description: 'Expected transaction ID'
3499
3955
  },
3500
- userId: {
3956
+ matchPayeePattern: {
3501
3957
  type: 'string',
3502
- description: 'User ID'
3958
+ description: 'Payee matching pattern'
3503
3959
  },
3504
- ruleId: {
3505
- type: 'string',
3506
- description: 'Associated rule ID'
3960
+ matchAmountTolerance: {
3961
+ type: 'number',
3962
+ description: 'Amount tolerance percentage'
3507
3963
  },
3508
- expectedDate: {
3964
+ defaultExpenseAccount: {
3509
3965
  type: 'string',
3510
- description: 'Expected date (YYYY-MM-DD)'
3966
+ description: 'Default expense account'
3511
3967
  },
3512
- expectedAmount: {
3513
- type: 'number',
3514
- description: 'Expected amount'
3968
+ defaultPaymentAccount: {
3969
+ type: 'string',
3970
+ description: 'Default payment account'
3515
3971
  },
3516
- status: {
3972
+ defaultPayee: {
3517
3973
  type: 'string',
3518
- description: 'Status (PENDING, COMPLETED, SKIPPED)'
3974
+ description: 'Default payee'
3519
3975
  },
3520
- matchedTransactionId: {
3521
- type: 'object',
3522
- description: 'Matched transaction ID'
3976
+ isActive: {
3977
+ type: 'boolean',
3978
+ description: 'Whether rule is active'
3523
3979
  },
3524
- matchedAt: {
3525
- type: 'object',
3526
- description: 'Match timestamp (ISO 8601)'
3980
+ startDate: {
3981
+ type: 'string',
3982
+ description: 'Rule start date (YYYY-MM-DD)'
3527
3983
  },
3528
- matchConfidence: {
3529
- type: 'object',
3530
- description: 'Match confidence score (0-1)'
3984
+ endDate: {
3985
+ type: 'string',
3986
+ description: 'Rule end date (YYYY-MM-DD)'
3531
3987
  },
3532
- isOverdue: {
3988
+ autoCreate: {
3533
3989
  type: 'boolean',
3534
- description: 'Whether this expected transaction is overdue'
3990
+ description: 'Auto-create transaction on expected date'
3535
3991
  },
3536
- rule: {
3537
- description: 'Rule information',
3538
- allOf: [
3539
- {
3540
- $ref: '#/components/schemas/ExpectedTransactionRuleDto'
3541
- }
3542
- ]
3992
+ lastOccurrence: {
3993
+ type: 'string',
3994
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
3995
+ },
3996
+ totalCount: {
3997
+ type: 'number',
3998
+ description: 'Total matched transactions count'
3543
3999
  },
3544
4000
  createdAt: {
3545
4001
  format: 'date-time',
@@ -3555,647 +4011,581 @@ export const $ExpectedTransactionResponseDto = {
3555
4011
  required: [
3556
4012
  'id',
3557
4013
  'userId',
3558
- 'ruleId',
3559
- 'expectedDate',
4014
+ 'name',
4015
+ 'frequency',
3560
4016
  'expectedAmount',
3561
- 'status',
3562
- 'isOverdue',
3563
- 'rule',
4017
+ 'currency',
4018
+ 'matchAmountTolerance',
4019
+ 'isActive',
4020
+ 'startDate',
4021
+ 'autoCreate',
4022
+ 'totalCount',
3564
4023
  'createdAt',
3565
4024
  'updatedAt'
3566
4025
  ]
3567
4026
  } as const;
3568
4027
 
3569
- export const $ExpectedTransactionListResponseDto = {
4028
+ export const $CreateRuleFromTransactionDto = {
3570
4029
  type: 'object',
3571
4030
  properties: {
3572
- items: {
3573
- type: 'array',
3574
- items: {
3575
- $ref: '#/components/schemas/ExpectedTransactionResponseDto'
3576
- }
4031
+ frequency: {
4032
+ type: 'string',
4033
+ description: 'Recurring frequency',
4034
+ enum: [
4035
+ 'WEEKLY',
4036
+ 'BIWEEKLY',
4037
+ 'MONTHLY',
4038
+ 'BIMONTHLY',
4039
+ 'QUARTERLY',
4040
+ 'YEARLY',
4041
+ 'CUSTOM'
4042
+ ],
4043
+ example: 'MONTHLY'
3577
4044
  },
3578
- total: {
3579
- type: 'number',
3580
- description: 'Total count'
3581
- }
3582
- },
3583
- required: ['items', 'total']
3584
- } as const;
3585
-
3586
- export const $ConfirmMatchDto = {
3587
- type: 'object',
3588
- properties: {
3589
- transactionId: {
4045
+ name: {
3590
4046
  type: 'string',
3591
- description: 'Transaction ID to match with'
4047
+ description: 'Optional name override (default: transaction payee)',
4048
+ maxLength: 100
4049
+ },
4050
+ icon: {
4051
+ type: 'string',
4052
+ description: 'Optional icon emoji',
4053
+ maxLength: 10
3592
4054
  }
3593
4055
  },
3594
- required: ['transactionId']
4056
+ required: ['frequency']
3595
4057
  } as const;
3596
4058
 
3597
- export const $EnterNowDto = {
4059
+ export const $RecurringRuleWithStatsResponseDto = {
3598
4060
  type: 'object',
3599
4061
  properties: {
3600
- expenseAccount: {
4062
+ id: {
3601
4063
  type: 'string',
3602
- description:
3603
- 'Override expense account (uses rule default if not provided)',
3604
- maxLength: 200
4064
+ description: 'Rule ID'
3605
4065
  },
3606
- paymentAccount: {
4066
+ userId: {
3607
4067
  type: 'string',
3608
- description:
3609
- 'Override payment account (uses rule default if not provided)',
3610
- maxLength: 200
4068
+ description: 'User ID'
3611
4069
  },
3612
- amount: {
3613
- type: 'number',
3614
- description: 'Override amount (uses expected amount if not provided)',
3615
- minimum: 0
4070
+ name: {
4071
+ type: 'string',
4072
+ description: 'Rule name'
3616
4073
  },
3617
- payee: {
4074
+ icon: {
3618
4075
  type: 'string',
3619
- description: 'Override payee (uses rule default if not provided)',
3620
- maxLength: 200
4076
+ description: 'Icon emoji'
3621
4077
  },
3622
- narration: {
4078
+ frequency: {
3623
4079
  type: 'string',
3624
- description: 'Optional narration',
3625
- maxLength: 500
3626
- }
3627
- }
3628
- } as const;
3629
-
3630
- export const $ForecastItemDto = {
3631
- type: 'object',
3632
- properties: {
3633
- rule: {
4080
+ description: 'Recurring frequency'
4081
+ },
4082
+ expectedAmount: {
4083
+ type: 'number',
4084
+ description: 'Expected amount'
4085
+ },
4086
+ expectedDay: {
4087
+ type: 'number',
4088
+ description: 'Expected day of month'
4089
+ },
4090
+ customIntervalDays: {
4091
+ type: 'number',
4092
+ description: 'Custom interval in days'
4093
+ },
4094
+ currency: {
3634
4095
  type: 'string',
3635
- description: 'Rule name',
3636
- example: 'Rent'
4096
+ description: 'Currency code'
3637
4097
  },
3638
- ruleId: {
4098
+ matchPayeePattern: {
3639
4099
  type: 'string',
3640
- description: 'Rule ID',
3641
- example: 'clx123...'
4100
+ description: 'Payee matching pattern'
3642
4101
  },
3643
- amount: {
4102
+ matchAmountTolerance: {
3644
4103
  type: 'number',
3645
- description: 'Expected amount',
3646
- example: 3000
4104
+ description: 'Amount tolerance percentage'
3647
4105
  },
3648
- date: {
4106
+ defaultExpenseAccount: {
3649
4107
  type: 'string',
3650
- description: 'Expected date (YYYY-MM-DD)',
3651
- example: '2024-04-01'
4108
+ description: 'Default expense account'
3652
4109
  },
3653
- icon: {
4110
+ defaultPaymentAccount: {
3654
4111
  type: 'string',
3655
- description: 'Rule icon emoji',
3656
- example: '🏠',
3657
- nullable: true
4112
+ description: 'Default payment account'
3658
4113
  },
3659
- currency: {
4114
+ defaultPayee: {
3660
4115
  type: 'string',
3661
- description: 'Currency code',
3662
- example: 'CNY'
3663
- }
3664
- },
3665
- required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
3666
- } as const;
3667
-
3668
- export const $MonthlyForecastDto = {
3669
- type: 'object',
3670
- properties: {
3671
- month: {
4116
+ description: 'Default payee'
4117
+ },
4118
+ isActive: {
4119
+ type: 'boolean',
4120
+ description: 'Whether rule is active'
4121
+ },
4122
+ startDate: {
3672
4123
  type: 'string',
3673
- description: 'Month (YYYY-MM)',
3674
- example: '2024-04'
4124
+ description: 'Rule start date (YYYY-MM-DD)'
3675
4125
  },
3676
- expectedOutflow: {
4126
+ endDate: {
4127
+ type: 'string',
4128
+ description: 'Rule end date (YYYY-MM-DD)'
4129
+ },
4130
+ autoCreate: {
4131
+ type: 'boolean',
4132
+ description: 'Auto-create transaction on expected date'
4133
+ },
4134
+ lastOccurrence: {
4135
+ type: 'string',
4136
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
4137
+ },
4138
+ totalCount: {
3677
4139
  type: 'number',
3678
- description: 'Total expected outflow for the month',
3679
- example: 8500
4140
+ description: 'Total matched transactions count'
3680
4141
  },
3681
- itemCount: {
4142
+ createdAt: {
4143
+ format: 'date-time',
4144
+ type: 'string',
4145
+ description: 'Created at timestamp'
4146
+ },
4147
+ updatedAt: {
4148
+ format: 'date-time',
4149
+ type: 'string',
4150
+ description: 'Updated at timestamp'
4151
+ },
4152
+ pendingCount: {
3682
4153
  type: 'number',
3683
- description: 'Number of expected transactions',
3684
- example: 3
4154
+ description: 'Number of pending expected transactions'
3685
4155
  },
3686
- byCurrency: {
3687
- type: 'object',
3688
- description: 'Breakdown by currency',
3689
- example: {
3690
- CNY: 8500,
3691
- USD: 100
3692
- }
4156
+ overdueCount: {
4157
+ type: 'number',
4158
+ description: 'Number of overdue expected transactions'
3693
4159
  },
3694
- items: {
3695
- description: 'Individual forecast items',
3696
- type: 'array',
3697
- items: {
3698
- $ref: '#/components/schemas/ForecastItemDto'
3699
- }
3700
- }
3701
- },
3702
- required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
3703
- } as const;
3704
-
3705
- export const $ForecastResponseDto = {
3706
- type: 'object',
3707
- properties: {
3708
- forecast: {
3709
- description: 'Monthly forecast data',
3710
- type: 'array',
3711
- items: {
3712
- $ref: '#/components/schemas/MonthlyForecastDto'
3713
- }
4160
+ nextExpectedDate: {
4161
+ type: 'string',
4162
+ description: 'Next expected date (YYYY-MM-DD)'
3714
4163
  },
3715
- totalOutflow: {
4164
+ totalAmount: {
3716
4165
  type: 'number',
3717
- description: 'Total expected outflow across all months',
3718
- example: 25500
4166
+ description: 'Total amount of all matched transactions'
3719
4167
  },
3720
- totalByCurrency: {
3721
- type: 'object',
3722
- description: 'Total by currency across all months',
3723
- example: {
3724
- CNY: 25500,
3725
- USD: 300
3726
- }
4168
+ averageAmount: {
4169
+ type: 'number',
4170
+ description: 'Average amount per transaction'
3727
4171
  },
3728
- rulesCount: {
4172
+ transactionCount: {
3729
4173
  type: 'number',
3730
- description: 'Number of active recurring rules included',
3731
- example: 5
4174
+ description: 'Number of matched transactions'
3732
4175
  },
3733
- periodStart: {
4176
+ firstDate: {
3734
4177
  type: 'string',
3735
- description: 'Forecast period start date',
3736
- example: '2024-04-01'
4178
+ description: 'First matched transaction date (YYYY-MM-DD)'
3737
4179
  },
3738
- periodEnd: {
4180
+ lastDate: {
3739
4181
  type: 'string',
3740
- description: 'Forecast period end date',
3741
- example: '2024-06-30'
4182
+ description: 'Last matched transaction date (YYYY-MM-DD)'
4183
+ },
4184
+ variance: {
4185
+ type: 'number',
4186
+ description: 'Amount variance (standard deviation squared)'
4187
+ },
4188
+ upcomingCount: {
4189
+ type: 'number',
4190
+ description: 'Number of upcoming expected transactions'
3742
4191
  }
3743
4192
  },
3744
4193
  required: [
3745
- 'forecast',
3746
- 'totalOutflow',
3747
- 'totalByCurrency',
3748
- 'rulesCount',
3749
- 'periodStart',
3750
- 'periodEnd'
4194
+ 'id',
4195
+ 'userId',
4196
+ 'name',
4197
+ 'frequency',
4198
+ 'expectedAmount',
4199
+ 'currency',
4200
+ 'matchAmountTolerance',
4201
+ 'isActive',
4202
+ 'startDate',
4203
+ 'autoCreate',
4204
+ 'totalCount',
4205
+ 'createdAt',
4206
+ 'updatedAt',
4207
+ 'pendingCount',
4208
+ 'overdueCount',
4209
+ 'totalAmount',
4210
+ 'averageAmount',
4211
+ 'transactionCount',
4212
+ 'variance',
4213
+ 'upcomingCount'
3751
4214
  ]
3752
4215
  } as const;
3753
4216
 
3754
- export const $CurrencyBalanceDto = {
3755
- type: 'object',
3756
- properties: {
3757
- currency: {
3758
- type: 'string',
3759
- description: 'ISO 4217 currency code',
3760
- example: 'CNY'
3761
- },
3762
- balance: {
3763
- type: 'string',
3764
- description: 'Balance amount',
3765
- example: '500000.00'
3766
- }
3767
- },
3768
- required: ['currency', 'balance']
3769
- } as const;
3770
-
3771
- export const $TimeSeriesPointDto = {
4217
+ export const $UpdateRecurringRuleDto = {
3772
4218
  type: 'object',
3773
4219
  properties: {
3774
- date: {
3775
- type: 'string',
3776
- description: 'Date in YYYY-MM-DD format',
3777
- example: '2024-06-15'
3778
- },
3779
- value: {
4220
+ name: {
3780
4221
  type: 'string',
3781
- description: 'Value at this date (in base currency)',
3782
- example: '500000.00'
3783
- },
3784
- change: {
3785
- type: 'object',
3786
- description: 'Change from previous point',
3787
- example: '5000.00'
4222
+ description: 'Rule name (unique per user)',
4223
+ maxLength: 100
3788
4224
  },
3789
- assets: {
4225
+ icon: {
3790
4226
  type: 'string',
3791
- description: 'Total assets at this date (in base currency)',
3792
- example: '494338.00'
4227
+ description: 'Icon emoji',
4228
+ maxLength: 10
3793
4229
  },
3794
- liabilities: {
4230
+ frequency: {
3795
4231
  type: 'string',
3796
- description: 'Total liabilities at this date (in base currency)',
3797
- example: '310098.00'
4232
+ description: 'Recurring frequency',
4233
+ enum: [
4234
+ 'WEEKLY',
4235
+ 'BIWEEKLY',
4236
+ 'MONTHLY',
4237
+ 'BIMONTHLY',
4238
+ 'QUARTERLY',
4239
+ 'YEARLY',
4240
+ 'CUSTOM'
4241
+ ]
3798
4242
  },
3799
- byCurrency: {
3800
- description: 'Multi-currency breakdown for this point',
3801
- type: 'array',
3802
- items: {
3803
- $ref: '#/components/schemas/CurrencyBalanceDto'
3804
- }
3805
- }
3806
- },
3807
- required: ['date', 'value']
3808
- } as const;
3809
-
3810
- export const $TrendSummaryDto = {
3811
- type: 'object',
3812
- properties: {
3813
- startValue: {
3814
- type: 'string',
3815
- description: 'Value at start of period',
3816
- example: '450000.00'
4243
+ expectedAmount: {
4244
+ type: 'number',
4245
+ description: 'Expected amount (positive number)',
4246
+ minimum: 0
3817
4247
  },
3818
- endValue: {
3819
- type: 'string',
3820
- description: 'Value at end of period',
3821
- example: '500000.00'
4248
+ expectedDay: {
4249
+ type: 'number',
4250
+ description: 'Expected day of month (1-31)',
4251
+ minimum: 1,
4252
+ maximum: 31
3822
4253
  },
3823
- totalChange: {
4254
+ currency: {
3824
4255
  type: 'string',
3825
- description: 'Total change over period',
3826
- example: '50000.00'
4256
+ description: 'Currency code',
4257
+ maxLength: 10
3827
4258
  },
3828
- totalChangePercentage: {
3829
- type: 'string',
3830
- description: 'Total change percentage',
3831
- example: '+11.11%'
3832
- }
3833
- },
3834
- required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
3835
- } as const;
3836
-
3837
- export const $MultiCurrencyPointDto = {
3838
- type: 'object',
3839
- properties: {
3840
- date: {
4259
+ matchPayeePattern: {
3841
4260
  type: 'string',
3842
- description: 'Date in YYYY-MM-DD format',
3843
- example: '2024-06-15'
3844
- },
3845
- byCurrency: {
3846
- description: 'Balances by currency',
3847
- type: 'array',
3848
- items: {
3849
- $ref: '#/components/schemas/CurrencyBalanceDto'
3850
- }
3851
- }
3852
- },
3853
- required: ['date', 'byCurrency']
3854
- } as const;
3855
-
3856
- export const $PortfolioTrendsResponseDto = {
3857
- type: 'object',
3858
- properties: {
3859
- series: {
3860
- description: 'Time series data points',
3861
- type: 'array',
3862
- items: {
3863
- $ref: '#/components/schemas/TimeSeriesPointDto'
3864
- }
4261
+ description: 'Payee matching pattern (supports wildcards)',
4262
+ maxLength: 200
3865
4263
  },
3866
- summary: {
3867
- description: 'Period summary',
3868
- allOf: [
3869
- {
3870
- $ref: '#/components/schemas/TrendSummaryDto'
3871
- }
3872
- ]
4264
+ matchAmountTolerance: {
4265
+ type: 'number',
4266
+ description: 'Amount tolerance percentage (0-1)',
4267
+ default: 0.075,
4268
+ minimum: 0,
4269
+ maximum: 1
3873
4270
  },
3874
- period: {
4271
+ defaultExpenseAccount: {
3875
4272
  type: 'string',
3876
- description: 'Period requested',
3877
- example: '6m'
4273
+ description: 'Default expense account for auto-create',
4274
+ maxLength: 200
3878
4275
  },
3879
- granularity: {
4276
+ defaultPaymentAccount: {
3880
4277
  type: 'string',
3881
- description: 'Data granularity',
3882
- example: 'month'
4278
+ description: 'Default payment account for auto-create',
4279
+ maxLength: 200
3883
4280
  },
3884
- currency: {
4281
+ defaultPayee: {
3885
4282
  type: 'string',
3886
- description: 'Base currency for converted values',
3887
- example: 'CNY'
4283
+ description: 'Default payee for auto-create',
4284
+ maxLength: 200
3888
4285
  },
3889
- byCurrency: {
3890
- description:
3891
- 'Multi-currency time series (each point has currency breakdown)',
3892
- type: 'array',
3893
- items: {
3894
- $ref: '#/components/schemas/MultiCurrencyPointDto'
3895
- }
4286
+ autoCreate: {
4287
+ type: 'boolean',
4288
+ description: 'Auto-create transaction when expected date arrives',
4289
+ default: false
3896
4290
  },
3897
- warnings: {
3898
- description: 'Exchange rate warnings',
3899
- type: 'array',
3900
- items: {
3901
- $ref: '#/components/schemas/ExchangeRateWarningDto'
3902
- }
4291
+ endDate: {
4292
+ type: 'string',
4293
+ description: 'Rule end date (ISO format)'
4294
+ },
4295
+ customIntervalDays: {
4296
+ type: 'number',
4297
+ description: 'Custom interval in days',
4298
+ minimum: 1
4299
+ },
4300
+ isActive: {
4301
+ type: 'boolean',
4302
+ description: 'Rule active status'
3903
4303
  }
3904
- },
3905
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4304
+ }
3906
4305
  } as const;
3907
4306
 
3908
- export const $CashFlowPointDto = {
4307
+ export const $ExpectedTransactionRuleDto = {
3909
4308
  type: 'object',
3910
4309
  properties: {
3911
- month: {
4310
+ name: {
3912
4311
  type: 'string',
3913
- description: 'Month key (YYYY-MM)',
3914
- example: '2024-03'
4312
+ description: 'Rule name'
3915
4313
  },
3916
- income: {
4314
+ icon: {
3917
4315
  type: 'string',
3918
- description: 'Income in base currency (absolute, converted)',
3919
- example: '10000.00'
4316
+ description: 'Rule icon'
3920
4317
  },
3921
- expense: {
4318
+ frequency: {
3922
4319
  type: 'string',
3923
- description: 'Expense in base currency (absolute, converted)',
3924
- example: '5000.00'
4320
+ description: 'Rule frequency'
3925
4321
  },
3926
- netSavings: {
4322
+ currency: {
3927
4323
  type: 'string',
3928
- description: 'netSavings = income − expense (savings positive)',
3929
- example: '5000.00'
4324
+ description: 'Currency code'
3930
4325
  }
3931
4326
  },
3932
- required: ['month', 'income', 'expense', 'netSavings']
4327
+ required: ['name', 'frequency', 'currency']
3933
4328
  } as const;
3934
4329
 
3935
- export const $CashFlowTrendSummaryDto = {
4330
+ export const $ExpectedTransactionResponseDto = {
3936
4331
  type: 'object',
3937
4332
  properties: {
3938
- totalIncome: {
4333
+ id: {
3939
4334
  type: 'string',
3940
- description: 'Total income across the period',
3941
- example: '60000.00'
4335
+ description: 'Expected transaction ID'
3942
4336
  },
3943
- totalExpense: {
4337
+ userId: {
3944
4338
  type: 'string',
3945
- description: 'Total expense across the period',
3946
- example: '30000.00'
4339
+ description: 'User ID'
3947
4340
  },
3948
- totalNetSavings: {
4341
+ ruleId: {
3949
4342
  type: 'string',
3950
- description: 'income − expense across the period',
3951
- example: '30000.00'
4343
+ description: 'Associated rule ID'
3952
4344
  },
3953
- averageMonthlyNetSavings: {
4345
+ expectedDate: {
3954
4346
  type: 'string',
3955
- description:
3956
- 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3957
- example: '5000.00'
3958
- }
3959
- },
3960
- required: [
3961
- 'totalIncome',
3962
- 'totalExpense',
3963
- 'totalNetSavings',
3964
- 'averageMonthlyNetSavings'
3965
- ]
3966
- } as const;
3967
-
3968
- export const $CashFlowTrendsResponseDto = {
3969
- type: 'object',
3970
- properties: {
3971
- series: {
3972
- description:
3973
- 'Monthly cash-flow series (fixed N-month window, zero-filled)',
3974
- type: 'array',
3975
- items: {
3976
- $ref: '#/components/schemas/CashFlowPointDto'
3977
- }
4347
+ description: 'Expected date (YYYY-MM-DD)'
3978
4348
  },
3979
- summary: {
3980
- description: 'Period totals',
4349
+ expectedAmount: {
4350
+ type: 'number',
4351
+ description: 'Expected amount'
4352
+ },
4353
+ status: {
4354
+ type: 'string',
4355
+ description: 'Status (PENDING, COMPLETED, SKIPPED)'
4356
+ },
4357
+ matchedTransactionId: {
4358
+ type: 'string',
4359
+ description: 'Matched transaction ID'
4360
+ },
4361
+ matchedAt: {
4362
+ type: 'string',
4363
+ description: 'Match timestamp (ISO 8601)'
4364
+ },
4365
+ matchConfidence: {
4366
+ type: 'number',
4367
+ description: 'Match confidence score (0-1)'
4368
+ },
4369
+ isOverdue: {
4370
+ type: 'boolean',
4371
+ description: 'Whether this expected transaction is overdue'
4372
+ },
4373
+ rule: {
4374
+ description: 'Rule information',
3981
4375
  allOf: [
3982
4376
  {
3983
- $ref: '#/components/schemas/CashFlowTrendSummaryDto'
4377
+ $ref: '#/components/schemas/ExpectedTransactionRuleDto'
3984
4378
  }
3985
4379
  ]
3986
4380
  },
3987
- period: {
3988
- type: 'string',
3989
- description: 'Period requested',
3990
- example: '6m'
3991
- },
3992
- granularity: {
4381
+ createdAt: {
4382
+ format: 'date-time',
3993
4383
  type: 'string',
3994
- description: 'Data granularity (v1 returns month buckets)',
3995
- example: 'month'
4384
+ description: 'Created at timestamp'
3996
4385
  },
3997
- currency: {
4386
+ updatedAt: {
4387
+ format: 'date-time',
3998
4388
  type: 'string',
3999
- description: 'Base currency for converted values',
4000
- example: 'CNY'
4001
- },
4002
- warnings: {
4003
- description: 'Exchange rate warnings (e.g. missing rate for a currency)',
4004
- type: 'array',
4005
- items: {
4006
- $ref: '#/components/schemas/ExchangeRateWarningDto'
4007
- }
4389
+ description: 'Updated at timestamp'
4008
4390
  }
4009
4391
  },
4010
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4011
- } as const;
4012
-
4013
- export const $GenerateSnapshotBody = {
4014
- type: 'object',
4015
- properties: {}
4016
- } as const;
4017
-
4018
- export const $GenerateSnapshotResponse = {
4019
- type: 'object',
4020
- properties: {}
4021
- } as const;
4022
-
4023
- export const $BackfillSnapshotsBody = {
4024
- type: 'object',
4025
- properties: {}
4026
- } as const;
4027
-
4028
- export const $BackfillSnapshotsResponse = {
4029
- type: 'object',
4030
- properties: {}
4392
+ required: [
4393
+ 'id',
4394
+ 'userId',
4395
+ 'ruleId',
4396
+ 'expectedDate',
4397
+ 'expectedAmount',
4398
+ 'status',
4399
+ 'isOverdue',
4400
+ 'rule',
4401
+ 'createdAt',
4402
+ 'updatedAt'
4403
+ ]
4031
4404
  } as const;
4032
4405
 
4033
- export const $DeleteOwnUserDto = {
4406
+ export const $ExpectedTransactionListResponseDto = {
4034
4407
  type: 'object',
4035
4408
  properties: {
4036
- accessToken: {
4037
- type: 'string',
4038
- description: 'Access token for user verification',
4039
- example: 'abc123xyz'
4409
+ items: {
4410
+ type: 'array',
4411
+ items: {
4412
+ $ref: '#/components/schemas/ExpectedTransactionResponseDto'
4413
+ }
4414
+ },
4415
+ total: {
4416
+ type: 'number',
4417
+ description: 'Total count'
4040
4418
  }
4041
4419
  },
4042
- required: ['accessToken']
4420
+ required: ['items', 'total']
4043
4421
  } as const;
4044
4422
 
4045
- export const $SignupDto = {
4423
+ export const $ConfirmMatchDto = {
4046
4424
  type: 'object',
4047
4425
  properties: {
4048
- turnstileToken: {
4426
+ transactionId: {
4049
4427
  type: 'string',
4050
- description:
4051
- 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4052
- example: '0.abc123def456...'
4428
+ description: 'Transaction ID to match with'
4053
4429
  }
4054
- }
4430
+ },
4431
+ required: ['transactionId']
4055
4432
  } as const;
4056
4433
 
4057
- export const $SignupResponseDto = {
4434
+ export const $EnterNowDto = {
4058
4435
  type: 'object',
4059
4436
  properties: {
4060
- authToken: {
4437
+ expenseAccount: {
4061
4438
  type: 'string',
4062
- description: 'JWT auth token',
4063
- example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
4439
+ description:
4440
+ 'Override expense account (uses rule default if not provided)',
4441
+ maxLength: 200
4064
4442
  },
4065
- accessToken: {
4443
+ paymentAccount: {
4066
4444
  type: 'string',
4067
- description: 'Auto-generated access token'
4445
+ description:
4446
+ 'Override payment account (uses rule default if not provided)',
4447
+ maxLength: 200
4448
+ },
4449
+ amount: {
4450
+ type: 'number',
4451
+ description: 'Override amount (uses expected amount if not provided)',
4452
+ minimum: 0
4453
+ },
4454
+ payee: {
4455
+ type: 'string',
4456
+ description: 'Override payee (uses rule default if not provided)',
4457
+ maxLength: 200
4068
4458
  },
4069
- role: {
4459
+ narration: {
4070
4460
  type: 'string',
4071
- description: 'Assigned user role',
4072
- enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
4461
+ description: 'Optional narration',
4462
+ maxLength: 500
4073
4463
  }
4074
- },
4075
- required: ['authToken', 'accessToken', 'role']
4464
+ }
4076
4465
  } as const;
4077
4466
 
4078
- export const $UpdateUserSettingDto = {
4467
+ export const $ForecastItemDto = {
4079
4468
  type: 'object',
4080
4469
  properties: {
4081
- secId: {
4082
- type: 'number',
4083
- description: 'Security ID'
4470
+ rule: {
4471
+ type: 'string',
4472
+ description: 'Rule name',
4473
+ example: 'Rent'
4084
4474
  },
4085
- annualInterestRate: {
4475
+ ruleId: {
4476
+ type: 'string',
4477
+ description: 'Rule ID',
4478
+ example: 'clx123...'
4479
+ },
4480
+ amount: {
4086
4481
  type: 'number',
4087
- description: 'Annual interest rate',
4088
- example: 0.05
4482
+ description: 'Expected amount',
4483
+ example: 3000
4089
4484
  },
4090
- currency: {
4485
+ date: {
4091
4486
  type: 'string',
4092
- description: 'Currency code',
4093
- example: 'USD'
4487
+ description: 'Expected date (YYYY-MM-DD)',
4488
+ example: '2024-04-01'
4094
4489
  },
4095
- baseCurrency: {
4490
+ icon: {
4096
4491
  type: 'string',
4097
- description: 'Base currency code',
4098
- example: 'USD'
4492
+ description: 'Rule icon emoji',
4493
+ example: '🏠',
4494
+ nullable: true
4099
4495
  },
4100
- benchmark: {
4496
+ currency: {
4101
4497
  type: 'string',
4102
- description: 'Benchmark symbol',
4103
- example: 'SPY'
4104
- },
4105
- colorScheme: {
4498
+ description: 'Currency code',
4499
+ example: 'CNY'
4500
+ }
4501
+ },
4502
+ required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
4503
+ } as const;
4504
+
4505
+ export const $MonthlyForecastDto = {
4506
+ type: 'object',
4507
+ properties: {
4508
+ month: {
4106
4509
  type: 'string',
4107
- description: 'Color scheme',
4108
- enum: ['DARK', 'LIGHT']
4510
+ description: 'Month (YYYY-MM)',
4511
+ example: '2024-04'
4109
4512
  },
4110
- dateRange: {
4111
- type: 'string',
4112
- description: 'Date range filter',
4113
- example: '1y'
4513
+ expectedOutflow: {
4514
+ type: 'number',
4515
+ description: 'Total expected outflow for the month',
4516
+ example: 8500
4114
4517
  },
4115
- emergencyFund: {
4518
+ itemCount: {
4116
4519
  type: 'number',
4117
- description: 'Emergency fund amount',
4118
- example: 10000
4520
+ description: 'Number of expected transactions',
4521
+ example: 3
4119
4522
  },
4120
- 'filters.accounts': {
4121
- description: 'Account filter IDs',
4122
- type: 'array',
4123
- items: {
4124
- type: 'string'
4523
+ byCurrency: {
4524
+ type: 'object',
4525
+ description: 'Breakdown by currency',
4526
+ example: {
4527
+ CNY: 8500,
4528
+ USD: 100
4125
4529
  }
4126
4530
  },
4127
- 'filters.assetClasses': {
4128
- description: 'Asset class filters',
4531
+ items: {
4532
+ description: 'Individual forecast items',
4129
4533
  type: 'array',
4130
4534
  items: {
4131
- type: 'string'
4535
+ $ref: '#/components/schemas/ForecastItemDto'
4132
4536
  }
4133
- },
4134
- 'filters.dataSource': {
4135
- type: 'string',
4136
- description: 'Data source filter'
4137
- },
4138
- 'filters.symbol': {
4139
- type: 'string',
4140
- description: 'Symbol filter'
4141
- },
4142
- 'filters.tags': {
4143
- description: 'Tag filters',
4537
+ }
4538
+ },
4539
+ required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
4540
+ } as const;
4541
+
4542
+ export const $ForecastResponseDto = {
4543
+ type: 'object',
4544
+ properties: {
4545
+ forecast: {
4546
+ description: 'Monthly forecast data',
4144
4547
  type: 'array',
4145
4548
  items: {
4146
- type: 'string'
4549
+ $ref: '#/components/schemas/MonthlyForecastDto'
4147
4550
  }
4148
4551
  },
4149
- isExperimentalFeatures: {
4150
- type: 'boolean',
4151
- description: 'Enable experimental features'
4152
- },
4153
- isRestrictedView: {
4154
- type: 'boolean',
4155
- description: 'Enable restricted view mode'
4156
- },
4157
- language: {
4158
- type: 'string',
4159
- description: 'Language code',
4160
- example: 'en'
4161
- },
4162
- locale: {
4163
- type: 'string',
4164
- description: 'Locale code',
4165
- example: 'en-US'
4166
- },
4167
- projectedTotalAmount: {
4552
+ totalOutflow: {
4168
4553
  type: 'number',
4169
- description: 'Projected total amount',
4170
- example: 1000000
4554
+ description: 'Total expected outflow across all months',
4555
+ example: 25500
4171
4556
  },
4172
- retirementDate: {
4173
- type: 'string',
4174
- description: 'Retirement date in ISO 8601 format',
4175
- example: '2050-01-01'
4557
+ totalByCurrency: {
4558
+ type: 'object',
4559
+ description: 'Total by currency across all months',
4560
+ example: {
4561
+ CNY: 25500,
4562
+ USD: 300
4563
+ }
4176
4564
  },
4177
- savingsRate: {
4565
+ rulesCount: {
4178
4566
  type: 'number',
4179
- description: 'Savings rate percentage',
4180
- example: 0.2
4567
+ description: 'Number of active recurring rules included',
4568
+ example: 5
4181
4569
  },
4182
- viewMode: {
4570
+ periodStart: {
4183
4571
  type: 'string',
4184
- description: 'View mode',
4185
- enum: ['DEFAULT', 'ZEN']
4186
- }
4187
- }
4188
- } as const;
4189
-
4190
- export const $UpdatePropertyDto = {
4191
- type: 'object',
4192
- properties: {
4193
- value: {
4572
+ description: 'Forecast period start date',
4573
+ example: '2024-04-01'
4574
+ },
4575
+ periodEnd: {
4194
4576
  type: 'string',
4195
- description: 'Property value'
4577
+ description: 'Forecast period end date',
4578
+ example: '2024-06-30'
4196
4579
  }
4197
4580
  },
4198
- required: ['value']
4581
+ required: [
4582
+ 'forecast',
4583
+ 'totalOutflow',
4584
+ 'totalByCurrency',
4585
+ 'rulesCount',
4586
+ 'periodStart',
4587
+ 'periodEnd'
4588
+ ]
4199
4589
  } as const;
4200
4590
 
4201
4591
  export const $CreateTransactionRuleDto = {
@@ -4757,7 +5147,8 @@ export const $UpdateTransactionRuleDto = {
4757
5147
  },
4758
5148
  matchLogic: {
4759
5149
  type: 'string',
4760
- enum: ['OR', 'AND']
5150
+ enum: ['OR', 'AND'],
5151
+ default: 'OR'
4761
5152
  },
4762
5153
  amountMin: {
4763
5154
  type: 'number',
@@ -4771,13 +5162,10 @@ export const $UpdateTransactionRuleDto = {
4771
5162
  },
4772
5163
  priority: {
4773
5164
  type: 'number',
5165
+ default: 50,
4774
5166
  minimum: 0,
4775
5167
  maximum: 1000
4776
5168
  },
4777
- enabled: {
4778
- type: 'boolean',
4779
- description: 'Enable or disable the rule'
4780
- },
4781
5169
  additionalTags: {
4782
5170
  items: {
4783
5171
  type: 'array'
@@ -4787,6 +5175,10 @@ export const $UpdateTransactionRuleDto = {
4787
5175
  },
4788
5176
  additionalMetadata: {
4789
5177
  type: 'object'
5178
+ },
5179
+ enabled: {
5180
+ type: 'boolean',
5181
+ description: 'Enable or disable the rule'
4790
5182
  }
4791
5183
  }
4792
5184
  } as const;
@@ -4815,36 +5207,113 @@ export const $TestRuleDto = {
4815
5207
  maxLength: 10
4816
5208
  }
4817
5209
  },
4818
- required: ['narration']
5210
+ required: ['narration']
5211
+ } as const;
5212
+
5213
+ export const $TestRuleResponseDto = {
5214
+ type: 'object',
5215
+ properties: {
5216
+ ruleId: {
5217
+ type: 'string',
5218
+ description: 'Rule ID that was tested'
5219
+ },
5220
+ matches: {
5221
+ type: 'boolean',
5222
+ description: 'Whether the rule matched the test data'
5223
+ },
5224
+ confidence: {
5225
+ type: 'number',
5226
+ description: 'Match confidence score (0-1)',
5227
+ example: 0.85
5228
+ },
5229
+ matchDetails: {
5230
+ type: 'object',
5231
+ description: 'Details of which fields matched',
5232
+ example: {
5233
+ narration: true,
5234
+ payee: false,
5235
+ categoryAccount: false
5236
+ }
5237
+ }
5238
+ },
5239
+ required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5240
+ } as const;
5241
+
5242
+ export const $CategoryCatalogEntryDto = {
5243
+ type: 'object',
5244
+ properties: {
5245
+ slug: {
5246
+ type: 'string',
5247
+ description: 'Category slug (single source-of-truth)',
5248
+ example: 'food'
5249
+ },
5250
+ scenario: {
5251
+ type: 'string',
5252
+ description: 'Display scenario group (maps to frontend picker _scenario)',
5253
+ enum: [
5254
+ 'expense',
5255
+ 'income',
5256
+ 'investment',
5257
+ 'banking',
5258
+ 'transfer',
5259
+ 'payment'
5260
+ ],
5261
+ example: 'expense'
5262
+ },
5263
+ icon: {
5264
+ type: 'string',
5265
+ description: 'Lucide icon name',
5266
+ example: 'utensils'
5267
+ },
5268
+ regions: {
5269
+ description: "Applicable regions ('*' = all, 'cn' = CN-only)",
5270
+ example: ['*'],
5271
+ type: 'array',
5272
+ items: {
5273
+ type: 'string'
5274
+ }
5275
+ },
5276
+ categoryAccounts: {
5277
+ description:
5278
+ "Beancount account paths (categoryAccount) of the region-enabled system rules whose categoryKeywords include this slug (#816). System rules only (public endpoint — user rules excluded); one-to-many by design (e.g. 'utilities' → Electricity/Water/Internet/Gas), sorted, [] when no rule maps the slug.",
5279
+ example: [
5280
+ 'Expenses:Utilities:Electricity',
5281
+ 'Expenses:Utilities:Gas',
5282
+ 'Expenses:Utilities:Internet',
5283
+ 'Expenses:Utilities:Water'
5284
+ ],
5285
+ type: 'array',
5286
+ items: {
5287
+ type: 'string'
5288
+ }
5289
+ }
5290
+ },
5291
+ required: ['slug', 'scenario', 'icon', 'regions', 'categoryAccounts']
4819
5292
  } as const;
4820
5293
 
4821
- export const $TestRuleResponseDto = {
5294
+ export const $CategoryCatalogListResponseDto = {
4822
5295
  type: 'object',
4823
5296
  properties: {
4824
- ruleId: {
4825
- type: 'string',
4826
- description: 'Rule ID that was tested'
4827
- },
4828
- matches: {
4829
- type: 'boolean',
4830
- description: 'Whether the rule matched the test data'
5297
+ items: {
5298
+ description: 'Category entries (region-scoped, query-filtered)',
5299
+ type: 'array',
5300
+ items: {
5301
+ $ref: '#/components/schemas/CategoryCatalogEntryDto'
5302
+ }
4831
5303
  },
4832
- confidence: {
5304
+ total: {
4833
5305
  type: 'number',
4834
- description: 'Match confidence score (0-1)',
4835
- example: 0.85
5306
+ description:
5307
+ 'Total category entries for the region (before query filtering)',
5308
+ example: 30
4836
5309
  },
4837
- matchDetails: {
4838
- type: 'object',
4839
- description: 'Details of which fields matched',
4840
- example: {
4841
- narration: true,
4842
- payee: false,
4843
- categoryAccount: false
4844
- }
5310
+ region: {
5311
+ type: 'string',
5312
+ description: 'Region code',
5313
+ example: 'cn'
4845
5314
  }
4846
5315
  },
4847
- required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5316
+ required: ['items', 'total', 'region']
4848
5317
  } as const;
4849
5318
 
4850
5319
  export const $CreateBeanEventDto = {
@@ -5010,6 +5479,14 @@ export const $OnboardingAccountDto = {
5010
5479
  description:
5011
5480
  'Platform ID to bind the account to (references Platform.id); omit for unbound',
5012
5481
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
5482
+ },
5483
+ displayName: {
5484
+ type: 'string',
5485
+ description:
5486
+ 'User-set display name override (omit/null = keep the derived name)',
5487
+ nullable: true,
5488
+ maxLength: 50,
5489
+ example: 'Salary card'
5013
5490
  }
5014
5491
  },
5015
5492
  required: ['path', 'currency']
@@ -5589,7 +6066,8 @@ export const $UpdateMapperDefaultsDto = {
5589
6066
  type: 'string',
5590
6067
  description: 'Source account for transactions (Beancount format)',
5591
6068
  example: 'Assets:CN:Alipay:Balance',
5592
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6069
+ pattern:
6070
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5593
6071
  },
5594
6072
  currency: {
5595
6073
  type: 'string',
@@ -5603,13 +6081,15 @@ export const $UpdateMapperDefaultsDto = {
5603
6081
  type: 'string',
5604
6082
  description: 'Default expense account (optional)',
5605
6083
  example: 'Expenses:Unknown',
5606
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6084
+ pattern:
6085
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5607
6086
  },
5608
6087
  incomeAccount: {
5609
6088
  type: 'string',
5610
6089
  description: 'Default income account (optional)',
5611
6090
  example: 'Income:Unknown',
5612
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6091
+ pattern:
6092
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5613
6093
  },
5614
6094
  methodAccountMapping: {
5615
6095
  type: 'object',
@@ -5666,12 +6146,14 @@ export const $ProviderSyncConfigDto = {
5666
6146
  },
5667
6147
  defaultExpenseAccount: {
5668
6148
  type: 'string',
5669
- description: 'Default expense account for the second posting',
6149
+ description:
6150
+ 'Default expense account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5670
6151
  example: 'Expenses:Unknown'
5671
6152
  },
5672
6153
  defaultIncomeAccount: {
5673
6154
  type: 'string',
5674
- description: 'Default income account for the second posting',
6155
+ description:
6156
+ 'Default income account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5675
6157
  example: 'Income:Unknown'
5676
6158
  },
5677
6159
  filterPending: {
@@ -5686,12 +6168,7 @@ export const $ProviderSyncConfigDto = {
5686
6168
  example: 'acc_gocardless_001'
5687
6169
  }
5688
6170
  },
5689
- required: [
5690
- 'sourceAccount',
5691
- 'defaultCurrency',
5692
- 'defaultExpenseAccount',
5693
- 'defaultIncomeAccount'
5694
- ]
6171
+ required: ['sourceAccount', 'defaultCurrency']
5695
6172
  } as const;
5696
6173
 
5697
6174
  export const $ProviderSyncDto = {
@@ -5901,15 +6378,106 @@ export const $UncoveredFormatMissDto = {
5901
6378
  properties: {}
5902
6379
  } as const;
5903
6380
 
6381
+ export const $ClientParsedDataDto = {
6382
+ type: 'object',
6383
+ properties: {
6384
+ amount: {
6385
+ type: 'number',
6386
+ description: 'Transaction amount',
6387
+ example: 35
6388
+ },
6389
+ currency: {
6390
+ type: 'string',
6391
+ description: 'Currency code',
6392
+ example: 'CNY'
6393
+ },
6394
+ date: {
6395
+ type: 'string',
6396
+ description: 'Transaction date (ISO 8601)',
6397
+ example: '2026-08-15'
6398
+ },
6399
+ payee: {
6400
+ type: 'string',
6401
+ description: 'Payee/merchant name',
6402
+ example: 'Starbucks'
6403
+ },
6404
+ narration: {
6405
+ type: 'string',
6406
+ description: 'Transaction narration'
6407
+ },
6408
+ category: {
6409
+ type: 'string',
6410
+ description:
6411
+ 'Category in canonical form (catalog slug or Stage-2 rule keyword token, ADR-0116) — echo back verbatim from parsedData.category. Foreign forms (locale display names, account paths) are rejected with nlp.category.invalid.',
6412
+ example: 'food'
6413
+ },
6414
+ incomeType: {
6415
+ type: 'string',
6416
+ description: 'Income type',
6417
+ example: 'Salary'
6418
+ },
6419
+ incomeSource: {
6420
+ type: 'string',
6421
+ description: 'Income source',
6422
+ example: 'Anthropic Inc.'
6423
+ },
6424
+ symbol: {
6425
+ type: 'string',
6426
+ description: 'Security symbol code (e.g., 600519, AAPL)',
6427
+ example: 'AAPL'
6428
+ },
6429
+ quantity: {
6430
+ type: 'number',
6431
+ description: 'Quantity of shares/units',
6432
+ example: 100
6433
+ },
6434
+ price: {
6435
+ type: 'number',
6436
+ description: 'Unit price per share/unit',
6437
+ example: 1900
6438
+ },
6439
+ investmentAction: {
6440
+ type: 'string',
6441
+ description: 'Investment action',
6442
+ enum: ['buy', 'sell'],
6443
+ example: 'buy'
6444
+ },
6445
+ paymentSource: {
6446
+ type: 'string',
6447
+ description: 'Payment source: asset (default) or liability (credit card)',
6448
+ enum: ['asset', 'liability'],
6449
+ example: 'asset'
6450
+ },
6451
+ liabilityHint: {
6452
+ type: 'string',
6453
+ description: 'Liability account hint (CreditCard/Huabei/Baitiao)',
6454
+ example: 'CreditCard'
6455
+ },
6456
+ warning: {
6457
+ type: 'string',
6458
+ description:
6459
+ 'Display-only warning from the prior response; accepted but ignored.',
6460
+ example: 'Cross-currency settlement applies.'
6461
+ }
6462
+ }
6463
+ } as const;
6464
+
5904
6465
  export const $ProcessNlpDto = {
5905
6466
  type: 'object',
5906
6467
  properties: {
5907
6468
  message: {
5908
6469
  type: 'string',
5909
- description: 'Natural language text describing a transaction (Chinese)',
5910
- example: 'yesterday Starbucks spent 35 yuan',
6470
+ description:
6471
+ 'Natural language text describing a transaction. Optional when `confirm` is true (structured confirm); otherwise required.',
6472
+ example: 'Starbucks 35',
5911
6473
  maxLength: 500
5912
6474
  },
6475
+ confirm: {
6476
+ type: 'boolean',
6477
+ description:
6478
+ 'Structured confirm signal — bypasses NL confirm-word matching when true. Send parsedData field edits alongside. The NL word-list path is the fallback.',
6479
+ example: true
6480
+ },
5913
6481
  sessionId: {
5914
6482
  type: 'string',
5915
6483
  description:
@@ -5917,17 +6485,51 @@ export const $ProcessNlpDto = {
5917
6485
  example: 'session_abc123'
5918
6486
  },
5919
6487
  parsedData: {
5920
- type: 'object',
5921
6488
  description:
5922
6489
  'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
5923
6490
  example: {
5924
6491
  amount: 35,
5925
6492
  currency: 'CNY',
5926
6493
  payee: 'Starbucks'
5927
- }
6494
+ },
6495
+ allOf: [
6496
+ {
6497
+ $ref: '#/components/schemas/ClientParsedDataDto'
6498
+ }
6499
+ ]
6500
+ },
6501
+ selectedRuleId: {
6502
+ type: 'string',
6503
+ description:
6504
+ '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.',
6505
+ example: 'rule_abc123'
6506
+ },
6507
+ selectedAccount: {
6508
+ type: 'string',
6509
+ description:
6510
+ 'confirm_account echo-back: account path selected from the prior confirm_account response (suggestedAccount, similarAccounts[i].path, or a typed path). Applied directly when the session is confirming_account — no NL re-parse.',
6511
+ example: 'Expenses:Food:Coffee'
6512
+ },
6513
+ viewpointAccount: {
6514
+ type: 'string',
6515
+ description:
6516
+ 'Viewpoint account hint: the beancount path the user drilled into (e.g. from an account drill-down). Tie-break only — never overrides accounts resolved from the text. Must be an owned, OPEN Assets:/Liabilities: account; unresolvable hints are silently ignored.',
6517
+ example: 'Assets:CN:Bank:ICBC'
6518
+ },
6519
+ viewpointCategory: {
6520
+ type: 'string',
6521
+ description:
6522
+ "Viewpoint category hint: the ADR-0075 Group segment the user drilled into (e.g. 'Food'). Resolved to a concrete OPEN account in that group; tie-break only — never overrides a category resolved from the text.",
6523
+ example: 'Food'
6524
+ },
6525
+ viewpointFlow: {
6526
+ type: 'string',
6527
+ description:
6528
+ "Companion flow root for viewpointCategory ('income' | 'expense'), mirroring the ADR-0126 list-endpoint invariant. Derived from the session's routed intent (multi-turn) when absent; a first-turn flow-less category hint is dropped — send the flow explicitly.",
6529
+ enum: ['income', 'expense'],
6530
+ example: 'expense'
5928
6531
  }
5929
- },
5930
- required: ['message']
6532
+ }
5931
6533
  } as const;
5932
6534
 
5933
6535
  export const $NlpTransactionInfoDto = {
@@ -5993,7 +6595,9 @@ export const $NlpParsedDataDto = {
5993
6595
  },
5994
6596
  category: {
5995
6597
  type: 'string',
5996
- description: 'Category'
6598
+ description:
6599
+ 'Category in canonical form: a CATEGORY_CATALOG slug (GET /{region}/bean/categories) or a Stage-2 rule categoryKeywords token (ADR-0116 D2). Never a locale display name or a beancount account path. Echo back verbatim on confirm.',
6600
+ example: 'food'
5997
6601
  },
5998
6602
  incomeType: {
5999
6603
  type: 'string',
@@ -6239,6 +6843,24 @@ export const $NlpRuleConfirmationDataDto = {
6239
6843
  ]
6240
6844
  } as const;
6241
6845
 
6846
+ export const $NlpAccountCandidateDto = {
6847
+ type: 'object',
6848
+ properties: {
6849
+ path: {
6850
+ type: 'string',
6851
+ description: 'Canonical beancount account path (echo back on selection)',
6852
+ example: 'Expenses:Food:Dining'
6853
+ },
6854
+ name: {
6855
+ type: 'string',
6856
+ description:
6857
+ 'Localized display name (ADR-0114 read-time projection, user locale)',
6858
+ example: '餐饮'
6859
+ }
6860
+ },
6861
+ required: ['path', 'name']
6862
+ } as const;
6863
+
6242
6864
  export const $NlpAccountConfirmationDataDto = {
6243
6865
  type: 'object',
6244
6866
  properties: {
@@ -6249,14 +6871,16 @@ export const $NlpAccountConfirmationDataDto = {
6249
6871
  },
6250
6872
  suggestedAccount: {
6251
6873
  type: 'string',
6252
- description: 'Suggested replacement account',
6874
+ description:
6875
+ 'Suggested replacement account (omitted when no clear candidate)',
6253
6876
  example: 'Expenses:Food:Drinks'
6254
6877
  },
6255
6878
  similarAccounts: {
6256
- description: 'Similar accounts for user selection',
6879
+ description:
6880
+ 'Similar accounts for user selection (path + localized name, #680)',
6257
6881
  type: 'array',
6258
6882
  items: {
6259
- type: 'string'
6883
+ $ref: '#/components/schemas/NlpAccountCandidateDto'
6260
6884
  }
6261
6885
  },
6262
6886
  errorMessage: {
@@ -6271,7 +6895,6 @@ export const $NlpAccountConfirmationDataDto = {
6271
6895
  },
6272
6896
  required: [
6273
6897
  'invalidAccount',
6274
- 'suggestedAccount',
6275
6898
  'similarAccounts',
6276
6899
  'errorMessage',
6277
6900
  'transactionContext'
@@ -6444,7 +7067,8 @@ export const $NlpSuggestedAccountDto = {
6444
7067
  },
6445
7068
  confidence: {
6446
7069
  type: 'number',
6447
- description: 'Confidence score for this suggestion (0-1)',
7070
+ description:
7071
+ 'Confidence score for this suggestion (0-1). Present = predicted (confirm/confirm_rule/confirm_account); omitted = actual persisted account (created). (#586)',
6448
7072
  example: 0.9
6449
7073
  }
6450
7074
  },
@@ -6480,23 +7104,31 @@ export const $NlpDefaultAccountsDto = {
6480
7104
  properties: {
6481
7105
  asset: {
6482
7106
  type: 'string',
6483
- description: 'Default asset account',
6484
- example: 'Assets:Checking'
7107
+ description:
7108
+ 'Default OPEN asset account (MRU when multiple), or null when none/ambiguous',
7109
+ example: 'Assets:Checking',
7110
+ nullable: true
6485
7111
  },
6486
7112
  expense: {
6487
7113
  type: 'string',
6488
- description: 'Default expense account',
6489
- example: 'Expenses:Uncategorized'
7114
+ description:
7115
+ 'Default OPEN expense account (MRU when multiple), or null when none/ambiguous',
7116
+ example: 'Expenses:Food:Coffee',
7117
+ nullable: true
6490
7118
  },
6491
7119
  income: {
6492
7120
  type: 'string',
6493
- description: 'Default income account',
6494
- example: 'Income:Uncategorized'
7121
+ description:
7122
+ 'Default OPEN income account (MRU when multiple), or null when none/ambiguous',
7123
+ example: 'Income:Salary',
7124
+ nullable: true
6495
7125
  },
6496
7126
  liability: {
6497
7127
  type: 'string',
6498
- description: 'Default liability account',
6499
- example: 'Liabilities:CreditCard'
7128
+ description:
7129
+ 'Default OPEN liability account (MRU when multiple), or null when none/ambiguous',
7130
+ example: 'Liabilities:CreditCard',
7131
+ nullable: true
6500
7132
  }
6501
7133
  },
6502
7134
  required: ['asset', 'expense', 'income', 'liability']
@@ -6536,7 +7168,7 @@ export const $NlpResponseDto = {
6536
7168
  type: 'string',
6537
7169
  description:
6538
7170
  'Asset sub-type (only present when intent is "asset"). Determines which asset-related form to render.',
6539
- enum: ['transfer', 'banking', 'investment'],
7171
+ enum: ['transfer', 'banking', 'investment', 'lend', 'lend_collect'],
6540
7172
  example: 'investment'
6541
7173
  },
6542
7174
  liabilitySubType: {
@@ -6684,7 +7316,7 @@ export const $NlpResponseDto = {
6684
7316
  },
6685
7317
  suggestedAccounts: {
6686
7318
  description:
6687
- 'Suggested accounts for this transaction. Contains recommended source and destination accounts based on the detected intent and rules.',
7319
+ '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.',
6688
7320
  allOf: [
6689
7321
  {
6690
7322
  $ref: '#/components/schemas/NlpSuggestedAccountsDto'
@@ -6693,7 +7325,7 @@ export const $NlpResponseDto = {
6693
7325
  },
6694
7326
  defaultAccounts: {
6695
7327
  description:
6696
- 'Default accounts for the user/region. These are fallback accounts used when no specific suggestion is available.',
7328
+ 'Default fallback accounts for the user/region (#586). v1 returns universal constants; per-user personalization is planned.',
6697
7329
  allOf: [
6698
7330
  {
6699
7331
  $ref: '#/components/schemas/NlpDefaultAccountsDto'
@@ -6739,13 +7371,26 @@ export const $PlatformListItemDto = {
6739
7371
  suggestedSegment: {
6740
7372
  type: 'string',
6741
7373
  description:
6742
- 'Suggested path segment — canonical with first char uppercased (ACC_COMP_NAME_RE)'
7374
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6743
7375
  },
6744
7376
  logoUrl: {
6745
7377
  type: 'string',
6746
7378
  description: 'Logo URL',
6747
7379
  nullable: true
6748
7380
  },
7381
+ countryCode: {
7382
+ type: 'string',
7383
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7384
+ example: 'CN',
7385
+ nullable: true
7386
+ },
7387
+ category: {
7388
+ type: 'string',
7389
+ description:
7390
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7391
+ nullable: true,
7392
+ example: 'DigitalWallet'
7393
+ },
6749
7394
  isBound: {
6750
7395
  type: 'boolean',
6751
7396
  description: 'Whether user has accounts using this platform'
@@ -6759,6 +7404,8 @@ export const $PlatformListItemDto = {
6759
7404
  'canonical',
6760
7405
  'suggestedSegment',
6761
7406
  'logoUrl',
7407
+ 'countryCode',
7408
+ 'category',
6762
7409
  'isBound'
6763
7410
  ]
6764
7411
  } as const;
@@ -6794,13 +7441,26 @@ export const $PlatformMatchResultDto = {
6794
7441
  suggestedSegment: {
6795
7442
  type: 'string',
6796
7443
  description:
6797
- 'Suggested path segment — canonical, already in ACCOUNT_RE format'
7444
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6798
7445
  },
6799
7446
  logoUrl: {
6800
7447
  type: 'string',
6801
7448
  description: 'Logo URL',
6802
7449
  nullable: true
6803
7450
  },
7451
+ countryCode: {
7452
+ type: 'string',
7453
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7454
+ example: 'CN',
7455
+ nullable: true
7456
+ },
7457
+ category: {
7458
+ type: 'string',
7459
+ description:
7460
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7461
+ nullable: true,
7462
+ example: 'DigitalWallet'
7463
+ },
6804
7464
  matchType: {
6805
7465
  type: 'string',
6806
7466
  description: "How this row matched: 'exact' > 'prefix' > 'substring'",
@@ -6814,6 +7474,8 @@ export const $PlatformMatchResultDto = {
6814
7474
  'type',
6815
7475
  'suggestedSegment',
6816
7476
  'logoUrl',
7477
+ 'countryCode',
7478
+ 'category',
6817
7479
  'matchType'
6818
7480
  ]
6819
7481
  } as const;
@@ -6843,7 +7505,80 @@ export const $PlatformMatchResponseDto = {
6843
7505
  description: 'true when total > platforms.length (more matches exist)'
6844
7506
  }
6845
7507
  },
6846
- required: ['platforms', 'matchType', 'total', 'hasMore']
7508
+ required: ['platforms', 'matchType', 'total', 'hasMore']
7509
+ } as const;
7510
+
7511
+ export const $PlatformStandardsPlatformDto = {
7512
+ type: 'object',
7513
+ properties: {
7514
+ id: {
7515
+ type: 'string',
7516
+ description: 'Global platform ID'
7517
+ },
7518
+ name: {
7519
+ type: 'string',
7520
+ description: 'Platform name (e.g., "ICBC")'
7521
+ },
7522
+ canonical: {
7523
+ type: 'string',
7524
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7525
+ },
7526
+ suggestedSegment: {
7527
+ type: 'string',
7528
+ description:
7529
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7530
+ },
7531
+ type: {
7532
+ type: 'string',
7533
+ description: 'Platform type',
7534
+ enum: [
7535
+ 'BANK',
7536
+ 'BROKERAGE',
7537
+ 'CRYPTO_EXCHANGE',
7538
+ 'PAYMENT',
7539
+ 'INVESTMENT',
7540
+ 'INSURANCE',
7541
+ 'OTHER'
7542
+ ]
7543
+ },
7544
+ category: {
7545
+ type: 'string',
7546
+ description:
7547
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank) resolved against the final region. null = no region-aware suggestion; fall back to type.',
7548
+ nullable: true,
7549
+ example: 'Bank'
7550
+ }
7551
+ },
7552
+ required: ['id', 'name', 'canonical', 'suggestedSegment', 'type', 'category']
7553
+ } as const;
7554
+
7555
+ export const $PlatformStandardsResponseDto = {
7556
+ type: 'object',
7557
+ properties: {
7558
+ platform: {
7559
+ description: 'The selected platform (institution lock source)',
7560
+ allOf: [
7561
+ {
7562
+ $ref: '#/components/schemas/PlatformStandardsPlatformDto'
7563
+ }
7564
+ ]
7565
+ },
7566
+ region: {
7567
+ type: 'string',
7568
+ description:
7569
+ "Resolved template region (ISO 3166-1 alpha-2, UPPERCASE): the platform's own countryCode when set, else the region query param. For regions without a regional template file the template list falls back to the universal-only catalog while region still echoes the code.",
7570
+ example: 'CN'
7571
+ },
7572
+ templates: {
7573
+ description:
7574
+ 'Candidate account-standard templates of the resolved region (groupable by productCategory client-side)',
7575
+ type: 'array',
7576
+ items: {
7577
+ $ref: '#/components/schemas/AccountStandardResponseDto'
7578
+ }
7579
+ }
7580
+ },
7581
+ required: ['platform', 'region', 'templates']
6847
7582
  } as const;
6848
7583
 
6849
7584
  export const $CreatePlatformDto = {
@@ -6947,7 +7682,8 @@ export const $UpdatePlatformDto = {
6947
7682
  },
6948
7683
  isActive: {
6949
7684
  type: 'boolean',
6950
- description: 'Whether the platform is active'
7685
+ description: 'Whether the platform is active',
7686
+ default: true
6951
7687
  }
6952
7688
  }
6953
7689
  } as const;
@@ -7110,7 +7846,8 @@ export const $AccountItemDto = {
7110
7846
  },
7111
7847
  displayName: {
7112
7848
  type: 'string',
7113
- description: 'Display name (last part of account path)',
7849
+ description:
7850
+ 'Display name: user-set name if provided (#762), else the ADR-0114 chain — request-locale catalog name, en pivot, then the last part of the account path (#771)',
7114
7851
  example: 'Savings'
7115
7852
  },
7116
7853
  balance: {
@@ -7146,7 +7883,8 @@ export const $PlatformGroupDto = {
7146
7883
  example: 'CMB Bank'
7147
7884
  },
7148
7885
  accounts: {
7149
- description: 'Accounts within this platform',
7886
+ description:
7887
+ 'Accounts within this platform (Assets and Liabilities rows, #696)',
7150
7888
  type: 'array',
7151
7889
  items: {
7152
7890
  $ref: '#/components/schemas/AccountItemDto'
@@ -7154,7 +7892,8 @@ export const $PlatformGroupDto = {
7154
7892
  },
7155
7893
  totalBalance: {
7156
7894
  type: 'string',
7157
- description: 'FX-converted total balance in base currency',
7895
+ description:
7896
+ 'FX-converted total balance in base currency (nets Assets + Liabilities rows; can be negative)',
7158
7897
  example: '100000.00'
7159
7898
  },
7160
7899
  balanceByCurrency: {
@@ -7173,7 +7912,7 @@ export const $PlatformGroupDto = {
7173
7912
  sharePct: {
7174
7913
  type: 'number',
7175
7914
  description:
7176
- 'Share of the grand converted total (0-100); 0 when grand total is 0',
7915
+ 'Share of the converted asset-side grand total (0-100); liability balances are excluded from the basis; 0 when grand total is 0 (#696)',
7177
7916
  example: 42.5
7178
7917
  }
7179
7918
  },
@@ -7221,7 +7960,8 @@ export const $AccountsSummaryDto = {
7221
7960
  properties: {
7222
7961
  totalAccounts: {
7223
7962
  type: 'number',
7224
- description: 'Total number of accounts'
7963
+ description:
7964
+ 'Total number of accounts (balance sheet: Assets + Liabilities, #696)'
7225
7965
  },
7226
7966
  totalPlatforms: {
7227
7967
  type: 'number',
@@ -7279,7 +8019,8 @@ export const $AccountItemWithAssetClassDto = {
7279
8019
  },
7280
8020
  displayName: {
7281
8021
  type: 'string',
7282
- description: 'Display name (last part of account path)',
8022
+ description:
8023
+ 'Display name: user-set name if provided (#762), else the ADR-0114 chain — request-locale catalog name, en pivot, then the last part of the account path (#771)',
7283
8024
  example: 'Savings'
7284
8025
  },
7285
8026
  balance: {
@@ -7765,7 +8506,7 @@ export const $MonetaryDto = {
7765
8506
  example: 'USD'
7766
8507
  },
7767
8508
  baseCcyEquivalent: {
7768
- type: 'object',
8509
+ type: 'string',
7769
8510
  description: 'Converted to user base currency (Decimal string)',
7770
8511
  example: '21600',
7771
8512
  nullable: true
@@ -7841,13 +8582,13 @@ export const $HoldingPnlRowDto = {
7841
8582
  example: 'Assets:US:Broker:AAPL'
7842
8583
  },
7843
8584
  accountCcy: {
7844
- type: 'object',
8585
+ type: 'string',
7845
8586
  description: 'Account settlement currency (ISO 4217), from cost currency',
7846
8587
  nullable: true,
7847
8588
  example: 'USD'
7848
8589
  },
7849
8590
  brokerType: {
7850
- type: 'object',
8591
+ type: 'string',
7851
8592
  description: 'Broker type derived from Platform.type',
7852
8593
  nullable: true,
7853
8594
  example: 'broker'
@@ -7868,7 +8609,7 @@ export const $HoldingPnlRowDto = {
7868
8609
  example: 'EQUITY'
7869
8610
  },
7870
8611
  assetSubClass: {
7871
- type: 'object',
8612
+ type: 'string',
7872
8613
  nullable: true,
7873
8614
  example: 'STOCK'
7874
8615
  },
@@ -7915,14 +8656,14 @@ export const $HoldingPnlRowDto = {
7915
8656
  ]
7916
8657
  },
7917
8658
  unrealizedPnlBase: {
7918
- type: 'object',
8659
+ type: 'string',
7919
8660
  description:
7920
8661
  'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
7921
8662
  nullable: true,
7922
8663
  example: '6000'
7923
8664
  },
7924
8665
  unrealizedPnlPct: {
7925
- type: 'object',
8666
+ type: 'string',
7926
8667
  description: 'Unrealized P&L % (Decimal string)',
7927
8668
  nullable: true,
7928
8669
  example: '25'
@@ -7946,7 +8687,7 @@ export const $HoldingPnlRowDto = {
7946
8687
  ]
7947
8688
  },
7948
8689
  pctOfInvestedAssets: {
7949
- type: 'object',
8690
+ type: 'string',
7950
8691
  description:
7951
8692
  'Share of invested assets % (Decimal string); only for invested chartTokens',
7952
8693
  nullable: true,
@@ -7991,15 +8732,15 @@ export const $HoldingPnlWarningDto = {
7991
8732
  ]
7992
8733
  },
7993
8734
  symbol: {
7994
- type: 'object',
8735
+ type: 'string',
7995
8736
  nullable: true
7996
8737
  },
7997
8738
  accountId: {
7998
- type: 'object',
8739
+ type: 'string',
7999
8740
  nullable: true
8000
8741
  },
8001
8742
  currency: {
8002
- type: 'object',
8743
+ type: 'string',
8003
8744
  nullable: true
8004
8745
  }
8005
8746
  },
@@ -8062,3 +8803,356 @@ export const $AnonymousLoginResponseDto = {
8062
8803
  },
8063
8804
  required: ['authToken']
8064
8805
  } as const;
8806
+
8807
+ export const $ParserContributionMetaDto = {
8808
+ type: 'object',
8809
+ properties: {
8810
+ institution: {
8811
+ type: 'string',
8812
+ description: 'Institution slug (lowercase kebab-case)',
8813
+ pattern: '^[a-z0-9]+(-[a-z0-9]+)*$',
8814
+ example: 'icbc'
8815
+ },
8816
+ region: {
8817
+ type: 'string',
8818
+ enum: [
8819
+ 'cn',
8820
+ 'us',
8821
+ 'de',
8822
+ 'fr',
8823
+ 'gb',
8824
+ 'hk',
8825
+ 'jp',
8826
+ 'sg',
8827
+ 'au',
8828
+ 'ca',
8829
+ 'other'
8830
+ ]
8831
+ },
8832
+ accountType: {
8833
+ type: 'string',
8834
+ enum: ['checking', 'savings', 'credit', 'debit', 'investment']
8835
+ },
8836
+ format: {
8837
+ type: 'string',
8838
+ enum: ['csv', 'xlsx', 'pdf', 'ofx', 'qif']
8839
+ },
8840
+ institutionDisplayName: {
8841
+ type: 'string',
8842
+ example: '中国工商银行'
8843
+ },
8844
+ encoding: {
8845
+ type: 'string',
8846
+ example: 'utf-8'
8847
+ },
8848
+ delimiter: {
8849
+ type: 'string',
8850
+ description: 'CSV delimiter character: ",", ";", "\\t" or "|"'
8851
+ },
8852
+ headerRows: {
8853
+ type: 'number',
8854
+ default: 1,
8855
+ description: 'Header row count; the client omits the field when it is 1'
8856
+ },
8857
+ notes: {
8858
+ type: 'string',
8859
+ maxLength: 2000
8860
+ }
8861
+ },
8862
+ required: ['institution', 'region', 'accountType', 'format']
8863
+ } as const;
8864
+
8865
+ export const $ParserContributionSamplesDto = {
8866
+ type: 'object',
8867
+ properties: {
8868
+ rows: {
8869
+ description:
8870
+ 'Client-sanitized sample rows (key = column name, value = cell)',
8871
+ type: 'array',
8872
+ items: {
8873
+ type: 'object'
8874
+ }
8875
+ },
8876
+ rawHeaders: {
8877
+ type: 'array',
8878
+ items: {
8879
+ type: 'string'
8880
+ }
8881
+ }
8882
+ },
8883
+ required: ['rows']
8884
+ } as const;
8885
+
8886
+ export const $FieldHintDto = {
8887
+ type: 'object',
8888
+ properties: {
8889
+ columnName: {
8890
+ type: 'string',
8891
+ example: '交易日期'
8892
+ },
8893
+ format: {
8894
+ type: 'string',
8895
+ description: 'Date format, e.g. yyyy-MM-dd HH:mm',
8896
+ example: 'yyyy-MM-dd'
8897
+ },
8898
+ signConvention: {
8899
+ type: 'string',
8900
+ enum: ['negative-expense', 'positive-expense', 'separate-columns']
8901
+ },
8902
+ creditColumn: {
8903
+ type: 'string'
8904
+ },
8905
+ debitColumn: {
8906
+ type: 'string'
8907
+ }
8908
+ },
8909
+ required: ['columnName']
8910
+ } as const;
8911
+
8912
+ export const $ParserContributionFieldHintsDto = {
8913
+ type: 'object',
8914
+ properties: {
8915
+ date: {
8916
+ $ref: '#/components/schemas/FieldHintDto'
8917
+ },
8918
+ amount: {
8919
+ $ref: '#/components/schemas/FieldHintDto'
8920
+ },
8921
+ description: {
8922
+ $ref: '#/components/schemas/FieldHintDto'
8923
+ },
8924
+ balance: {
8925
+ $ref: '#/components/schemas/FieldHintDto'
8926
+ },
8927
+ payee: {
8928
+ $ref: '#/components/schemas/FieldHintDto'
8929
+ },
8930
+ reference: {
8931
+ $ref: '#/components/schemas/FieldHintDto'
8932
+ },
8933
+ category: {
8934
+ $ref: '#/components/schemas/FieldHintDto'
8935
+ }
8936
+ },
8937
+ required: ['date', 'amount']
8938
+ } as const;
8939
+
8940
+ export const $ExpectedTransactionDto = {
8941
+ type: 'object',
8942
+ properties: {
8943
+ date: {
8944
+ type: 'string',
8945
+ example: '2026-08-01'
8946
+ },
8947
+ amount: {
8948
+ type: 'number',
8949
+ example: -45.5
8950
+ },
8951
+ description: {
8952
+ type: 'string',
8953
+ example: '星巴克-***店'
8954
+ },
8955
+ payee: {
8956
+ type: 'string'
8957
+ },
8958
+ category: {
8959
+ type: 'string'
8960
+ }
8961
+ },
8962
+ required: ['date', 'amount', 'description']
8963
+ } as const;
8964
+
8965
+ export const $ParserContributionExamplesDto = {
8966
+ type: 'object',
8967
+ properties: {
8968
+ expectedTransactions: {
8969
+ type: 'array',
8970
+ items: {
8971
+ $ref: '#/components/schemas/ExpectedTransactionDto'
8972
+ }
8973
+ }
8974
+ },
8975
+ required: ['expectedTransactions']
8976
+ } as const;
8977
+
8978
+ export const $ParserContributionRequestDto = {
8979
+ type: 'object',
8980
+ properties: {
8981
+ meta: {
8982
+ $ref: '#/components/schemas/ParserContributionMetaDto'
8983
+ },
8984
+ samples: {
8985
+ $ref: '#/components/schemas/ParserContributionSamplesDto'
8986
+ },
8987
+ fieldHints: {
8988
+ $ref: '#/components/schemas/ParserContributionFieldHintsDto'
8989
+ },
8990
+ examples: {
8991
+ description: 'Omitted entirely by the client when empty',
8992
+ allOf: [
8993
+ {
8994
+ $ref: '#/components/schemas/ParserContributionExamplesDto'
8995
+ }
8996
+ ]
8997
+ }
8998
+ },
8999
+ required: ['meta', 'samples', 'fieldHints']
9000
+ } as const;
9001
+
9002
+ export const $ParserContributionRelayResponseDto = {
9003
+ type: 'object',
9004
+ properties: {
9005
+ issueUrl: {
9006
+ type: 'string',
9007
+ example: 'https://github.com/fire-zu/firela-vlt/issues/42'
9008
+ },
9009
+ issueNumber: {
9010
+ type: 'number',
9011
+ example: 42
9012
+ }
9013
+ },
9014
+ required: ['issueUrl', 'issueNumber']
9015
+ } as const;
9016
+
9017
+ export const $SymbolSearchResultDto = {
9018
+ type: 'object',
9019
+ properties: {
9020
+ symbol: {
9021
+ type: 'string',
9022
+ example: 'AAPL'
9023
+ },
9024
+ name: {
9025
+ type: 'string',
9026
+ example: 'Apple Inc.',
9027
+ nullable: true
9028
+ },
9029
+ exchange: {
9030
+ type: 'string',
9031
+ example: 'US',
9032
+ nullable: true
9033
+ },
9034
+ assetType: {
9035
+ type: 'string',
9036
+ description: 'OpenBB asset_type (e.g. stock, etf)',
9037
+ example: 'stock',
9038
+ nullable: true
9039
+ },
9040
+ assetClass: {
9041
+ type: 'string',
9042
+ description: 'IGN asset class (region.types.ts ASSET_CLASSES)',
9043
+ example: 'EQUITY',
9044
+ nullable: true
9045
+ },
9046
+ assetSubClass: {
9047
+ type: 'string',
9048
+ description: 'IGN asset sub-class (region.types.ts ASSET_SUB_CLASSES)',
9049
+ example: 'STOCK',
9050
+ nullable: true
9051
+ },
9052
+ currency: {
9053
+ type: 'string',
9054
+ description: 'Trading currency (extra_data or inferred from exchange)',
9055
+ example: 'USD',
9056
+ nullable: true
9057
+ }
9058
+ },
9059
+ required: ['symbol']
9060
+ } as const;
9061
+
9062
+ export const $SymbolQuoteDto = {
9063
+ type: 'object',
9064
+ properties: {
9065
+ symbol: {
9066
+ type: 'string',
9067
+ example: 'AAPL'
9068
+ },
9069
+ name: {
9070
+ type: 'string',
9071
+ example: 'Apple Inc.',
9072
+ nullable: true
9073
+ },
9074
+ exchange: {
9075
+ type: 'string',
9076
+ example: 'US',
9077
+ nullable: true
9078
+ },
9079
+ assetType: {
9080
+ type: 'string',
9081
+ description: 'OpenBB asset_type',
9082
+ example: 'stock',
9083
+ nullable: true
9084
+ },
9085
+ assetClass: {
9086
+ type: 'string',
9087
+ description: 'IGN asset class',
9088
+ example: 'EQUITY',
9089
+ nullable: true
9090
+ },
9091
+ assetSubClass: {
9092
+ type: 'string',
9093
+ description: 'IGN asset sub-class',
9094
+ example: 'STOCK',
9095
+ nullable: true
9096
+ },
9097
+ currency: {
9098
+ type: 'string',
9099
+ description: 'Trading currency (extra_data or inferred from exchange)',
9100
+ example: 'USD',
9101
+ nullable: true
9102
+ },
9103
+ price: {
9104
+ type: 'string',
9105
+ description: 'Latest price (Decimal string)',
9106
+ example: '189.84',
9107
+ nullable: true
9108
+ },
9109
+ priceDate: {
9110
+ type: 'string',
9111
+ description: 'Date the price was observed (ISO yyyy-MM-dd)',
9112
+ example: '2026-08-05',
9113
+ nullable: true
9114
+ },
9115
+ changePercent: {
9116
+ type: 'number',
9117
+ description:
9118
+ '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.',
9119
+ example: 1.7,
9120
+ nullable: true
9121
+ },
9122
+ prevClose: {
9123
+ type: 'string',
9124
+ description: 'Previous close (Decimal string)',
9125
+ nullable: true
9126
+ },
9127
+ open: {
9128
+ type: 'string',
9129
+ description: 'Day open (Decimal string)',
9130
+ nullable: true
9131
+ },
9132
+ high: {
9133
+ type: 'string',
9134
+ description: 'Day high (Decimal string)',
9135
+ nullable: true
9136
+ },
9137
+ low: {
9138
+ type: 'string',
9139
+ description: 'Day low (Decimal string)',
9140
+ nullable: true
9141
+ },
9142
+ volume: {
9143
+ type: 'string',
9144
+ description: 'Day volume (Decimal string)',
9145
+ nullable: true
9146
+ },
9147
+ yearHigh: {
9148
+ type: 'string',
9149
+ description: '52-week high (Decimal string)',
9150
+ nullable: true
9151
+ },
9152
+ yearLow: {
9153
+ type: 'string',
9154
+ description: '52-week low (Decimal string)',
9155
+ nullable: true
9156
+ }
9157
+ }
9158
+ } as const;