@firela/api-types 0.0.0-canary.97006feb → 0.0.0-canary.98d93318

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.
@@ -88,6 +88,49 @@ export const $AccountResponseDto = {
88
88
  enum: ['Assets', 'Liabilities', 'Income', 'Expenses', 'Equity'],
89
89
  example: 'Assets'
90
90
  },
91
+ assetSubClass: {
92
+ type: 'string',
93
+ description:
94
+ 'Account-level asset sub-class (product type, e.g. STOCK/DEPOSIT/CREDIT_CARD/PERSONAL_LOAN). Computed from the account path via the asset-classifier (ADR-0077). Null for non-asset accounts (Income/Expenses/Equity) or unmatched paths.',
95
+ enum: [
96
+ 'DEPOSIT',
97
+ 'CASH',
98
+ 'MONEY_MARKET_FUND',
99
+ 'STOCK',
100
+ 'ETF',
101
+ 'MUTUAL_FUND',
102
+ 'EQUITY_COMPENSATION',
103
+ 'GOVERNMENT_BOND',
104
+ 'CORPORATE_BOND',
105
+ 'BOND_FUND',
106
+ 'PRIMARY_RESIDENCE',
107
+ 'INVESTMENT_PROPERTY',
108
+ 'REIT',
109
+ 'GOLD',
110
+ 'SILVER',
111
+ 'PRECIOUS_METAL',
112
+ 'PRECIOUS_METAL_FUND',
113
+ 'COMMODITY',
114
+ 'COMMODITY_FUND',
115
+ 'CRYPTOCURRENCY',
116
+ 'RETIREMENT_ACCOUNT',
117
+ 'HEALTH_ACCOUNT',
118
+ 'EDUCATION_ACCOUNT',
119
+ 'INSURANCE',
120
+ 'PRIVATE_EQUITY',
121
+ 'HEDGE_FUND',
122
+ 'COLLECTIBLES',
123
+ 'MORTGAGE',
124
+ 'STUDENT_LOAN',
125
+ 'CREDIT_CARD',
126
+ 'PERSONAL_LOAN',
127
+ 'ACCOUNTS_PAYABLE',
128
+ 'TAX_PAYABLE',
129
+ 'OTHER'
130
+ ],
131
+ nullable: true,
132
+ example: 'STOCK'
133
+ },
91
134
  status: {
92
135
  type: 'string',
93
136
  description: 'Account status',
@@ -154,7 +197,7 @@ export const $AccountResponseDto = {
154
197
  }
155
198
  },
156
199
  platformId: {
157
- type: 'object',
200
+ type: 'string',
158
201
  description: 'Platform ID (null if unbound)',
159
202
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
160
203
  },
@@ -334,16 +377,43 @@ export const $AccountStandardResponseDto = {
334
377
  },
335
378
  name: {
336
379
  type: 'string',
337
- description: 'Short localized display name',
380
+ description:
381
+ '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
382
  example: 'Housing Fund'
339
383
  },
384
+ aliases: {
385
+ description:
386
+ '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.',
387
+ example: ['Alipay', 'WeChat Pay'],
388
+ type: 'array',
389
+ items: {
390
+ type: 'string'
391
+ }
392
+ },
393
+ searchTerms: {
394
+ description:
395
+ '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).',
396
+ example: ['yinhangka', 'jiejika'],
397
+ type: 'array',
398
+ items: {
399
+ type: 'string'
400
+ }
401
+ },
402
+ currency: {
403
+ type: 'string',
404
+ description:
405
+ '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).',
406
+ example: 'HKD'
407
+ },
340
408
  description: {
341
409
  type: 'string',
342
- description: 'Account description (stable semantics only)',
410
+ description:
411
+ '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
412
  example: 'ICBC checking account for daily transactions'
344
413
  },
345
414
  tags: {
346
- description: 'Account tags for categorization',
415
+ description:
416
+ 'Account tags for categorization — structured metadata delivered verbatim (not localized, not xlf-managed). ADR-0131 class A.',
347
417
  example: ['bank', 'checking', 'primary'],
348
418
  type: 'array',
349
419
  items: {
@@ -354,9 +424,47 @@ export const $AccountStandardResponseDto = {
354
424
  type: 'string',
355
425
  description: 'Icon identifier for UI display',
356
426
  example: 'bank-icbc'
427
+ },
428
+ productCategory: {
429
+ type: 'string',
430
+ description:
431
+ 'Onboarding product category (coarse grouping derived from assetSubClass)',
432
+ enum: [
433
+ 'cash',
434
+ 'investment',
435
+ 'credit_card',
436
+ 'loan',
437
+ 'payable_tax',
438
+ 'other'
439
+ ],
440
+ example: 'investment'
441
+ },
442
+ assetClass: {
443
+ type: 'string',
444
+ description:
445
+ 'Asset class (LIQUIDITY/EQUITY/.../LIABILITY), derived at read time from classification rules',
446
+ enum: [
447
+ 'LIQUIDITY',
448
+ 'EQUITY',
449
+ 'FIXED_INCOME',
450
+ 'PRECIOUS_METALS',
451
+ 'COMMODITY',
452
+ 'INSURANCE',
453
+ 'ALTERNATIVE_INVESTMENT',
454
+ 'PERSONAL_ASSETS',
455
+ 'LIABILITY',
456
+ 'REAL_ESTATE',
457
+ 'INDEX'
458
+ ]
459
+ },
460
+ assetSubClass: {
461
+ type: 'string',
462
+ description:
463
+ 'Asset sub-class (product type, derived at read time from classification rules)',
464
+ example: 'STOCK'
357
465
  }
358
466
  },
359
- required: ['path', 'type', 'description', 'tags', 'icon']
467
+ required: ['path', 'type', 'description', 'tags', 'icon', 'productCategory']
360
468
  } as const;
361
469
 
362
470
  export const $AccountStandardListResponseDto = {
@@ -417,7 +525,10 @@ export const $RegionConfigDto = {
417
525
  },
418
526
  locale: {
419
527
  type: 'string',
420
- example: 'de-DE'
528
+ example: 'de-DE',
529
+ pattern: '^[a-z]{2,8}-[A-Z]{2}$',
530
+ description:
531
+ "Region-qualified BCP-47 tag whose region subtag equals the region's own ISO 3166-1 code (e.g., ja-JP, zh-CN, en-HK)"
421
532
  }
422
533
  },
423
534
  required: ['currency', 'dateFormat', 'locale']
@@ -430,6 +541,12 @@ export const $RegionInfoDto = {
430
541
  type: 'string',
431
542
  example: 'de'
432
543
  },
544
+ open: {
545
+ type: 'boolean',
546
+ example: true,
547
+ description:
548
+ '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.'
549
+ },
433
550
  displayName: {
434
551
  type: 'string',
435
552
  example: 'Germany'
@@ -448,7 +565,7 @@ export const $RegionInfoDto = {
448
565
  $ref: '#/components/schemas/RegionConfigDto'
449
566
  }
450
567
  },
451
- required: ['code', 'displayName', 'chain', 'config']
568
+ required: ['code', 'open', 'displayName', 'chain', 'config']
452
569
  } as const;
453
570
 
454
571
  export const $RegionsMetadataResponseDto = {
@@ -695,7 +812,7 @@ export const $PostingResponseDto = {
695
812
  units: {
696
813
  type: 'string',
697
814
  description:
698
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
815
+ '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
816
  example: '100.50'
700
817
  },
701
818
  currency: {
@@ -1061,7 +1178,7 @@ export const $PostingDetailDto = {
1061
1178
  units: {
1062
1179
  type: 'string',
1063
1180
  description:
1064
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
1181
+ '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
1182
  example: '100.50'
1066
1183
  },
1067
1184
  currency: {
@@ -1245,6 +1362,147 @@ export const $TransactionDetailDto = {
1245
1362
  ]
1246
1363
  } as const;
1247
1364
 
1365
+ export const $TransactionListItemDto = {
1366
+ type: 'object',
1367
+ properties: {
1368
+ id: {
1369
+ type: 'string',
1370
+ description: 'Transaction ID',
1371
+ example: 'clh1234567890abcdef'
1372
+ },
1373
+ date: {
1374
+ type: 'string',
1375
+ description: 'Transaction date',
1376
+ example: '2024-11-28'
1377
+ },
1378
+ flag: {
1379
+ type: 'string',
1380
+ description: 'Transaction flag',
1381
+ enum: [
1382
+ 'CLEARED',
1383
+ 'PENDING',
1384
+ 'PADDING',
1385
+ 'SUMMARIZE',
1386
+ 'TRANSFER',
1387
+ 'CONVERSIONS'
1388
+ ],
1389
+ example: 'CLEARED'
1390
+ },
1391
+ customFlag: {
1392
+ type: 'string',
1393
+ description: 'Custom flag (if not using standard flags)',
1394
+ example: 'R'
1395
+ },
1396
+ payee: {
1397
+ type: 'string',
1398
+ description: 'Payee name',
1399
+ example: 'Whole Foods Market'
1400
+ },
1401
+ narration: {
1402
+ type: 'string',
1403
+ description: 'Transaction narration',
1404
+ example: 'Grocery shopping'
1405
+ },
1406
+ tags: {
1407
+ description: 'Transaction tags',
1408
+ example: ['groceries'],
1409
+ type: 'array',
1410
+ items: {
1411
+ type: 'string'
1412
+ }
1413
+ },
1414
+ links: {
1415
+ description: 'Transaction links',
1416
+ example: ['invoice-2024-001'],
1417
+ type: 'array',
1418
+ items: {
1419
+ type: 'string'
1420
+ }
1421
+ },
1422
+ meta: {
1423
+ type: 'object',
1424
+ description: 'Transaction metadata'
1425
+ },
1426
+ status: {
1427
+ type: 'string',
1428
+ description: 'Transaction status',
1429
+ enum: ['ACTIVE', 'VOIDED', 'SUPERSEDED'],
1430
+ example: 'ACTIVE'
1431
+ },
1432
+ sourceType: {
1433
+ type: 'string',
1434
+ description:
1435
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1436
+ },
1437
+ sourcePlatform: {
1438
+ type: 'string',
1439
+ description: 'Source platform (e.g., alipay, wechat)',
1440
+ example: 'alipay'
1441
+ },
1442
+ postings: {
1443
+ description: 'Transaction postings',
1444
+ type: 'array',
1445
+ items: {
1446
+ $ref: '#/components/schemas/PostingDetailDto'
1447
+ }
1448
+ },
1449
+ createdAt: {
1450
+ type: 'string',
1451
+ description: 'Created at timestamp',
1452
+ example: '2024-11-28T10:30:00.000Z'
1453
+ },
1454
+ voidedAt: {
1455
+ type: 'string',
1456
+ description: 'Voided at timestamp (if voided)',
1457
+ example: '2024-11-29T15:00:00.000Z'
1458
+ },
1459
+ voidedBy: {
1460
+ type: 'string',
1461
+ description: 'User ID who voided this transaction',
1462
+ example: 'clh1234567890abcdef'
1463
+ },
1464
+ correctionReason: {
1465
+ type: 'string',
1466
+ description: 'Correction reason (if voided or superseded)',
1467
+ example: 'Duplicate entry'
1468
+ },
1469
+ supersededBy: {
1470
+ type: 'string',
1471
+ description:
1472
+ 'ID of the transaction that supersedes this one (set when status=SUPERSEDED)',
1473
+ example: 'clh1234567890abcdef'
1474
+ },
1475
+ originalTxn: {
1476
+ type: 'string',
1477
+ description:
1478
+ 'ID of the transaction this one corrected/replaced (back-link on the replacement)',
1479
+ example: 'clh1234567890abcdef'
1480
+ },
1481
+ viewpointAmount: {
1482
+ type: 'string',
1483
+ description:
1484
+ '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.',
1485
+ example: '10000.00'
1486
+ },
1487
+ viewpointCurrency: {
1488
+ type: 'string',
1489
+ description:
1490
+ 'Currency of viewpointAmount. A row spanning multiple currencies takes the largest-magnitude currency group (known simplification, ADR-0126).',
1491
+ example: 'CNY'
1492
+ }
1493
+ },
1494
+ required: [
1495
+ 'id',
1496
+ 'date',
1497
+ 'narration',
1498
+ 'tags',
1499
+ 'links',
1500
+ 'status',
1501
+ 'postings',
1502
+ 'createdAt'
1503
+ ]
1504
+ } as const;
1505
+
1248
1506
  export const $BalanceByCurrencyDto = {
1249
1507
  type: 'object',
1250
1508
  properties: {
@@ -1290,7 +1548,7 @@ export const $TransactionListSummaryDto = {
1290
1548
  totalAmount: {
1291
1549
  type: 'string',
1292
1550
  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.',
1551
+ '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
1552
  example: '-6000.00'
1295
1553
  },
1296
1554
  currency: {
@@ -1316,6 +1574,26 @@ export const $TransactionListSummaryDto = {
1316
1574
  required: ['totalAmount', 'currency', 'balanceByCurrency']
1317
1575
  } as const;
1318
1576
 
1577
+ export const $TransactionListViewpointDto = {
1578
+ type: 'object',
1579
+ properties: {
1580
+ type: {
1581
+ type: 'string',
1582
+ description:
1583
+ 'Viewpoint type (only category drill-down carries a viewpoint today)',
1584
+ enum: ['category'],
1585
+ example: 'category'
1586
+ },
1587
+ flow: {
1588
+ type: 'string',
1589
+ description: 'Flow root the category account set is restricted to',
1590
+ enum: ['income', 'expense'],
1591
+ example: 'expense'
1592
+ }
1593
+ },
1594
+ required: ['type', 'flow']
1595
+ } as const;
1596
+
1319
1597
  export const $TransactionListResponseDto = {
1320
1598
  type: 'object',
1321
1599
  properties: {
@@ -1323,7 +1601,7 @@ export const $TransactionListResponseDto = {
1323
1601
  description: 'List of transactions',
1324
1602
  type: 'array',
1325
1603
  items: {
1326
- $ref: '#/components/schemas/TransactionDetailDto'
1604
+ $ref: '#/components/schemas/TransactionListItemDto'
1327
1605
  }
1328
1606
  },
1329
1607
  total: {
@@ -1349,6 +1627,15 @@ export const $TransactionListResponseDto = {
1349
1627
  $ref: '#/components/schemas/TransactionListSummaryDto'
1350
1628
  }
1351
1629
  ]
1630
+ },
1631
+ viewpoint: {
1632
+ description:
1633
+ '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).',
1634
+ allOf: [
1635
+ {
1636
+ $ref: '#/components/schemas/TransactionListViewpointDto'
1637
+ }
1638
+ ]
1352
1639
  }
1353
1640
  },
1354
1641
  required: ['data', 'total', 'limit', 'offset']
@@ -2259,10 +2546,10 @@ export const $UpdatePayeeDto = {
2259
2546
  meta: {
2260
2547
  type: 'object',
2261
2548
  description:
2262
- 'Metadata for extended information (location, notes, contact info, etc.). Will merge with existing metadata.',
2549
+ 'Metadata for extended information (location, notes, contact info, etc.)',
2263
2550
  example: {
2264
2551
  location: 'Zhongguancun',
2265
- note: 'Updated note',
2552
+ note: 'Near subway station',
2266
2553
  favorite: true
2267
2554
  }
2268
2555
  },
@@ -2823,568 +3110,660 @@ export const $UpdateCommodityDto = {
2823
3110
  }
2824
3111
  } as const;
2825
3112
 
2826
- export const $CreateBeanPriceDto = {
3113
+ export const $CurrencyBalanceDto = {
2827
3114
  type: 'object',
2828
3115
  properties: {
2829
3116
  currency: {
2830
3117
  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)',
3118
+ description: 'ISO 4217 currency code',
2837
3119
  example: 'CNY'
2838
3120
  },
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: {
3121
+ balance: {
2847
3122
  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
- }
3123
+ description: 'Balance amount',
3124
+ example: '500000.00'
2860
3125
  }
2861
3126
  },
2862
- required: ['currency', 'quoteCurrency', 'amount', 'date']
3127
+ required: ['currency', 'balance']
2863
3128
  } as const;
2864
3129
 
2865
- export const $PriceResponseDto = {
3130
+ export const $TimeSeriesPointDto = {
2866
3131
  type: 'object',
2867
3132
  properties: {
2868
- id: {
3133
+ date: {
2869
3134
  type: 'string',
2870
- description: 'Unique identifier',
2871
- example: 'uuid-123-456'
3135
+ description: 'Date in YYYY-MM-DD format',
3136
+ example: '2024-06-15'
2872
3137
  },
2873
- userId: {
3138
+ value: {
2874
3139
  type: 'string',
2875
- description: 'User ID (owner of the price)',
2876
- example: 'user-123'
3140
+ description: 'Value at this date (in base currency)',
3141
+ example: '500000.00'
2877
3142
  },
2878
- currency: {
3143
+ change: {
2879
3144
  type: 'string',
2880
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2881
- example: 'BTC'
3145
+ description: 'Change from previous point',
3146
+ example: '5000.00'
2882
3147
  },
2883
- quoteCurrency: {
3148
+ assets: {
2884
3149
  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
3150
+ description: 'Total assets at this date (in base currency)',
3151
+ example: '494338.00'
2893
3152
  },
2894
- date: {
3153
+ liabilities: {
2895
3154
  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'
3155
+ description: 'Total liabilities at this date (in base currency)',
3156
+ example: '310098.00'
2900
3157
  },
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
3158
+ byCurrency: {
3159
+ description: 'Multi-currency breakdown for this point',
3160
+ type: 'array',
3161
+ items: {
3162
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2909
3163
  }
3164
+ }
3165
+ },
3166
+ required: ['date', 'value']
3167
+ } as const;
3168
+
3169
+ export const $TrendSummaryDto = {
3170
+ type: 'object',
3171
+ properties: {
3172
+ startValue: {
3173
+ type: 'string',
3174
+ description: 'Value at start of period',
3175
+ example: '450000.00'
2910
3176
  },
2911
- createdAt: {
2912
- format: 'date-time',
3177
+ endValue: {
2913
3178
  type: 'string',
2914
- description: 'Creation timestamp',
2915
- example: '2024-11-03T10:00:00Z'
3179
+ description: 'Value at end of period',
3180
+ example: '500000.00'
2916
3181
  },
2917
- updatedAt: {
2918
- format: 'date-time',
3182
+ totalChange: {
2919
3183
  type: 'string',
2920
- description: 'Last update timestamp',
2921
- example: '2024-11-03T10:00:00Z'
3184
+ description: 'Total change over period',
3185
+ example: '50000.00'
3186
+ },
3187
+ totalChangePercentage: {
3188
+ type: 'string',
3189
+ description: 'Total change percentage',
3190
+ example: '+11.11%'
2922
3191
  }
2923
3192
  },
2924
- required: [
2925
- 'id',
2926
- 'userId',
2927
- 'currency',
2928
- 'quoteCurrency',
2929
- 'amount',
2930
- 'date',
2931
- 'meta',
2932
- 'createdAt',
2933
- 'updatedAt'
2934
- ]
3193
+ required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
2935
3194
  } as const;
2936
3195
 
2937
- export const $PriceListResponseDto = {
3196
+ export const $MultiCurrencyPointDto = {
2938
3197
  type: 'object',
2939
3198
  properties: {
2940
- items: {
2941
- description: 'List of prices',
3199
+ date: {
3200
+ type: 'string',
3201
+ description: 'Date in YYYY-MM-DD format',
3202
+ example: '2024-06-15'
3203
+ },
3204
+ byCurrency: {
3205
+ description: 'Balances by currency',
2942
3206
  type: 'array',
2943
3207
  items: {
2944
- $ref: '#/components/schemas/PriceResponseDto'
3208
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2945
3209
  }
2946
- },
2947
- total: {
2948
- type: 'number',
2949
- description: 'Total number of prices',
2950
- example: 42
2951
3210
  }
2952
3211
  },
2953
- required: ['items', 'total']
3212
+ required: ['date', 'byCurrency']
2954
3213
  } as const;
2955
3214
 
2956
- export const $UpdateBeanPriceDto = {
3215
+ export const $PortfolioTrendsResponseDto = {
2957
3216
  type: 'object',
2958
3217
  properties: {
2959
- currency: {
2960
- type: 'string',
2961
- description: 'Currency being priced'
3218
+ series: {
3219
+ description: 'Time series data points',
3220
+ type: 'array',
3221
+ items: {
3222
+ $ref: '#/components/schemas/TimeSeriesPointDto'
3223
+ }
2962
3224
  },
2963
- quoteCurrency: {
3225
+ summary: {
3226
+ description: 'Period summary',
3227
+ allOf: [
3228
+ {
3229
+ $ref: '#/components/schemas/TrendSummaryDto'
3230
+ }
3231
+ ]
3232
+ },
3233
+ period: {
2964
3234
  type: 'string',
2965
- description: 'Quote currency (pricing currency)'
3235
+ description: 'Period requested',
3236
+ example: '6m'
2966
3237
  },
2967
- amount: {
2968
- type: 'number',
2969
- description: 'Price amount (MUST be >= 0 per Beancount spec)',
2970
- minimum: 0
3238
+ granularity: {
3239
+ type: 'string',
3240
+ description: 'Data granularity',
3241
+ example: 'month'
2971
3242
  },
2972
- date: {
3243
+ currency: {
2973
3244
  type: 'string',
2974
- description: 'Price date (ISO 8601 format)'
3245
+ description: 'Base currency for converted values',
3246
+ example: 'CNY'
2975
3247
  },
2976
- metadata: {
2977
- type: 'object',
2978
- description: 'Metadata'
3248
+ byCurrency: {
3249
+ description:
3250
+ 'Multi-currency time series (each point has currency breakdown)',
3251
+ type: 'array',
3252
+ items: {
3253
+ $ref: '#/components/schemas/MultiCurrencyPointDto'
3254
+ }
3255
+ },
3256
+ warnings: {
3257
+ description: 'Exchange rate warnings',
3258
+ type: 'array',
3259
+ items: {
3260
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3261
+ }
2979
3262
  }
2980
- }
3263
+ },
3264
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
2981
3265
  } as const;
2982
3266
 
2983
- export const $CreateRecurringRuleDto = {
3267
+ export const $CashFlowPointDto = {
2984
3268
  type: 'object',
2985
3269
  properties: {
2986
- name: {
3270
+ month: {
2987
3271
  type: 'string',
2988
- description: 'Rule name (unique per user)',
2989
- maxLength: 100
3272
+ description: 'Month key (YYYY-MM)',
3273
+ example: '2024-03'
2990
3274
  },
2991
- icon: {
3275
+ income: {
2992
3276
  type: 'string',
2993
- description: 'Icon emoji',
2994
- maxLength: 10
3277
+ description: 'Income in base currency (absolute, converted)',
3278
+ example: '10000.00'
2995
3279
  },
2996
- frequency: {
3280
+ expense: {
2997
3281
  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
3013
- },
3014
- expectedDay: {
3015
- type: 'number',
3016
- description: 'Expected day of month (1-31)',
3017
- minimum: 1,
3018
- maximum: 31
3019
- },
3020
- customIntervalDays: {
3021
- type: 'number',
3022
- description: 'Custom interval in days (required for CUSTOM frequency)',
3023
- minimum: 1
3282
+ description: 'Expense in base currency (absolute, converted)',
3283
+ example: '5000.00'
3024
3284
  },
3025
- currency: {
3285
+ netSavings: {
3026
3286
  type: 'string',
3027
- description: 'Currency code',
3028
- default: 'CNY',
3029
- maxLength: 10
3030
- },
3031
- matchPayeePattern: {
3287
+ description: 'netSavings = income − expense (savings positive)',
3288
+ example: '5000.00'
3289
+ }
3290
+ },
3291
+ required: ['month', 'income', 'expense', 'netSavings']
3292
+ } as const;
3293
+
3294
+ export const $CashFlowTrendSummaryDto = {
3295
+ type: 'object',
3296
+ properties: {
3297
+ totalIncome: {
3032
3298
  type: 'string',
3033
- description: 'Payee matching pattern (supports wildcards)',
3034
- maxLength: 200
3035
- },
3036
- matchAmountTolerance: {
3037
- type: 'number',
3038
- description: 'Amount tolerance percentage (0-1)',
3039
- default: 0.075,
3040
- minimum: 0,
3041
- maximum: 1
3299
+ description: 'Total income across the period',
3300
+ example: '60000.00'
3042
3301
  },
3043
- defaultExpenseAccount: {
3302
+ totalExpense: {
3044
3303
  type: 'string',
3045
- description: 'Default expense account for auto-create',
3046
- maxLength: 200
3304
+ description: 'Total expense across the period',
3305
+ example: '30000.00'
3047
3306
  },
3048
- defaultPaymentAccount: {
3307
+ totalNetSavings: {
3049
3308
  type: 'string',
3050
- description: 'Default payment account for auto-create',
3051
- maxLength: 200
3309
+ description: 'income − expense across the period',
3310
+ example: '30000.00'
3052
3311
  },
3053
- defaultPayee: {
3054
- type: 'string',
3055
- description: 'Default payee for auto-create',
3056
- maxLength: 200
3057
- },
3058
- autoCreate: {
3059
- type: 'boolean',
3060
- description: 'Auto-create transaction when expected date arrives',
3061
- default: false
3062
- },
3063
- startDate: {
3064
- type: 'string',
3065
- description: 'Rule start date (ISO format)'
3066
- },
3067
- endDate: {
3312
+ averageMonthlyNetSavings: {
3068
3313
  type: 'string',
3069
- description: 'Rule end date (ISO format)'
3314
+ description:
3315
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3316
+ example: '5000.00'
3070
3317
  }
3071
3318
  },
3072
3319
  required: [
3073
- 'name',
3074
- 'frequency',
3075
- 'expectedAmount',
3076
- 'currency',
3077
- 'matchAmountTolerance',
3078
- 'autoCreate'
3320
+ 'totalIncome',
3321
+ 'totalExpense',
3322
+ 'totalNetSavings',
3323
+ 'averageMonthlyNetSavings'
3079
3324
  ]
3080
3325
  } as const;
3081
3326
 
3082
- export const $RecurringRuleResponseDto = {
3327
+ export const $CashFlowTrendsResponseDto = {
3083
3328
  type: 'object',
3084
3329
  properties: {
3085
- id: {
3086
- type: 'string',
3087
- description: 'Rule ID'
3330
+ series: {
3331
+ description:
3332
+ 'Monthly cash-flow series (fixed N-month window, zero-filled)',
3333
+ type: 'array',
3334
+ items: {
3335
+ $ref: '#/components/schemas/CashFlowPointDto'
3336
+ }
3088
3337
  },
3089
- userId: {
3090
- type: 'string',
3091
- description: 'User ID'
3338
+ summary: {
3339
+ description: 'Period totals',
3340
+ allOf: [
3341
+ {
3342
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
3343
+ }
3344
+ ]
3092
3345
  },
3093
- name: {
3346
+ period: {
3094
3347
  type: 'string',
3095
- description: 'Rule name'
3096
- },
3097
- icon: {
3098
- type: 'object',
3099
- description: 'Icon emoji'
3348
+ description: 'Period requested',
3349
+ example: '6m'
3100
3350
  },
3101
- frequency: {
3351
+ granularity: {
3102
3352
  type: 'string',
3103
- description: 'Recurring frequency'
3104
- },
3105
- expectedAmount: {
3106
- type: 'number',
3107
- description: 'Expected amount'
3108
- },
3109
- expectedDay: {
3110
- type: 'object',
3111
- description: 'Expected day of month'
3353
+ description: 'Data granularity (v1 returns month buckets)',
3354
+ example: 'month'
3112
3355
  },
3113
- customIntervalDays: {
3114
- type: 'object',
3115
- description: 'Custom interval in days'
3356
+ currency: {
3357
+ type: 'string',
3358
+ description: 'Base currency for converted values',
3359
+ example: 'CNY'
3116
3360
  },
3361
+ warnings: {
3362
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
3363
+ type: 'array',
3364
+ items: {
3365
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3366
+ }
3367
+ }
3368
+ },
3369
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3370
+ } as const;
3371
+
3372
+ export const $GenerateSnapshotBody = {
3373
+ type: 'object',
3374
+ properties: {}
3375
+ } as const;
3376
+
3377
+ export const $GenerateSnapshotResponse = {
3378
+ type: 'object',
3379
+ properties: {}
3380
+ } as const;
3381
+
3382
+ export const $BackfillSnapshotsBody = {
3383
+ type: 'object',
3384
+ properties: {}
3385
+ } as const;
3386
+
3387
+ export const $BackfillSnapshotsResponse = {
3388
+ type: 'object',
3389
+ properties: {}
3390
+ } as const;
3391
+
3392
+ export const $CreateBeanPriceDto = {
3393
+ type: 'object',
3394
+ properties: {
3117
3395
  currency: {
3118
3396
  type: 'string',
3119
- description: 'Currency code'
3397
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3398
+ example: 'USD'
3120
3399
  },
3121
- matchPayeePattern: {
3122
- type: 'object',
3123
- description: 'Payee matching pattern'
3400
+ quoteCurrency: {
3401
+ type: 'string',
3402
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3403
+ example: 'CNY'
3124
3404
  },
3125
- matchAmountTolerance: {
3405
+ amount: {
3126
3406
  type: 'number',
3127
- description: 'Amount tolerance percentage'
3407
+ description:
3408
+ 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
3409
+ example: 175.5,
3410
+ minimum: 0
3128
3411
  },
3129
- defaultExpenseAccount: {
3130
- type: 'object',
3131
- description: 'Default expense account'
3412
+ date: {
3413
+ type: 'string',
3414
+ description: 'Price date (ISO 8601 format)',
3415
+ example: '2024-11-05'
3132
3416
  },
3133
- defaultPaymentAccount: {
3417
+ metadata: {
3134
3418
  type: 'object',
3135
- description: 'Default payment account'
3419
+ description:
3420
+ 'Metadata (validated by Zod schema, max field lengths enforced)',
3421
+ example: {
3422
+ source: 'MANUAL',
3423
+ note: 'Bank valuation report',
3424
+ confidence: 0.95
3425
+ }
3426
+ }
3427
+ },
3428
+ required: ['currency', 'quoteCurrency', 'amount', 'date']
3429
+ } as const;
3430
+
3431
+ export const $PriceResponseDto = {
3432
+ type: 'object',
3433
+ properties: {
3434
+ id: {
3435
+ type: 'string',
3436
+ description: 'Unique identifier',
3437
+ example: 'uuid-123-456'
3136
3438
  },
3137
- defaultPayee: {
3138
- type: 'object',
3139
- description: 'Default payee'
3439
+ userId: {
3440
+ type: 'string',
3441
+ description: 'User ID (owner of the price)',
3442
+ example: 'user-123'
3140
3443
  },
3141
- isActive: {
3142
- type: 'boolean',
3143
- description: 'Whether rule is active'
3444
+ currency: {
3445
+ type: 'string',
3446
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3447
+ example: 'BTC'
3144
3448
  },
3145
- startDate: {
3449
+ quoteCurrency: {
3146
3450
  type: 'string',
3147
- description: 'Rule start date (YYYY-MM-DD)'
3451
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
3452
+ example: 'USD'
3148
3453
  },
3149
- endDate: {
3150
- type: 'object',
3151
- description: 'Rule end date (YYYY-MM-DD)'
3454
+ amount: {
3455
+ type: 'number',
3456
+ description:
3457
+ 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
3458
+ example: 50000
3152
3459
  },
3153
- autoCreate: {
3154
- type: 'boolean',
3155
- description: 'Auto-create transaction on expected date'
3460
+ date: {
3461
+ type: 'string',
3462
+ description:
3463
+ 'Price date (ISO 8601 format). Represents the date this price was valid.',
3464
+ example: '2024-01-01',
3465
+ format: 'date'
3156
3466
  },
3157
- lastOccurrence: {
3467
+ meta: {
3158
3468
  type: 'object',
3159
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3160
- },
3161
- totalCount: {
3162
- type: 'number',
3163
- description: 'Total matched transactions count'
3469
+ description:
3470
+ 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
3471
+ example: {
3472
+ source: 'MANUAL',
3473
+ note: 'User-defined price',
3474
+ confidence: 1
3475
+ }
3164
3476
  },
3165
3477
  createdAt: {
3166
3478
  format: 'date-time',
3167
3479
  type: 'string',
3168
- description: 'Created at timestamp'
3480
+ description: 'Creation timestamp',
3481
+ example: '2024-11-03T10:00:00Z'
3169
3482
  },
3170
3483
  updatedAt: {
3171
3484
  format: 'date-time',
3172
3485
  type: 'string',
3173
- description: 'Updated at timestamp'
3486
+ description: 'Last update timestamp',
3487
+ example: '2024-11-03T10:00:00Z'
3174
3488
  }
3175
3489
  },
3176
3490
  required: [
3177
3491
  'id',
3178
3492
  'userId',
3179
- 'name',
3180
- 'frequency',
3181
- 'expectedAmount',
3182
3493
  'currency',
3183
- 'matchAmountTolerance',
3184
- 'isActive',
3185
- 'startDate',
3186
- 'autoCreate',
3187
- 'totalCount',
3494
+ 'quoteCurrency',
3495
+ 'amount',
3496
+ 'date',
3497
+ 'meta',
3188
3498
  'createdAt',
3189
3499
  'updatedAt'
3190
3500
  ]
3191
3501
  } as const;
3192
3502
 
3193
- export const $CreateRuleFromTransactionDto = {
3503
+ export const $PriceListResponseDto = {
3194
3504
  type: 'object',
3195
3505
  properties: {
3196
- frequency: {
3197
- 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'
3209
- },
3210
- name: {
3211
- type: 'string',
3212
- description: 'Optional name override (default: transaction payee)',
3213
- maxLength: 100
3506
+ items: {
3507
+ description: 'List of prices',
3508
+ type: 'array',
3509
+ items: {
3510
+ $ref: '#/components/schemas/PriceResponseDto'
3511
+ }
3214
3512
  },
3215
- icon: {
3216
- type: 'string',
3217
- description: 'Optional icon emoji',
3218
- maxLength: 10
3513
+ total: {
3514
+ type: 'number',
3515
+ description: 'Total number of prices',
3516
+ example: 42
3219
3517
  }
3220
3518
  },
3221
- required: ['frequency']
3519
+ required: ['items', 'total']
3222
3520
  } as const;
3223
3521
 
3224
- export const $RecurringRuleWithStatsResponseDto = {
3522
+ export const $UpdateBeanPriceDto = {
3225
3523
  type: 'object',
3226
3524
  properties: {
3227
- id: {
3525
+ currency: {
3228
3526
  type: 'string',
3229
- description: 'Rule ID'
3527
+ description: 'Currency being priced'
3230
3528
  },
3231
- userId: {
3529
+ quoteCurrency: {
3232
3530
  type: 'string',
3233
- description: 'User ID'
3531
+ description: 'Quote currency (pricing currency)'
3234
3532
  },
3235
- name: {
3533
+ amount: {
3534
+ type: 'number',
3535
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
3536
+ minimum: 0
3537
+ },
3538
+ date: {
3236
3539
  type: 'string',
3237
- description: 'Rule name'
3540
+ description: 'Price date (ISO 8601 format)'
3238
3541
  },
3239
- icon: {
3542
+ metadata: {
3240
3543
  type: 'object',
3241
- description: 'Icon emoji'
3242
- },
3243
- frequency: {
3544
+ description: 'Metadata'
3545
+ }
3546
+ }
3547
+ } as const;
3548
+
3549
+ export const $DeleteOwnUserDto = {
3550
+ type: 'object',
3551
+ properties: {
3552
+ accessToken: {
3244
3553
  type: 'string',
3245
- description: 'Recurring frequency'
3246
- },
3247
- expectedAmount: {
3248
- type: 'number',
3249
- description: 'Expected amount'
3554
+ description: 'Access token for user verification',
3555
+ example: 'abc123xyz'
3556
+ }
3557
+ },
3558
+ required: ['accessToken']
3559
+ } as const;
3560
+
3561
+ export const $UserSettingsResponseDto = {
3562
+ type: 'object',
3563
+ properties: {
3564
+ baseCurrency: {
3565
+ type: 'string',
3566
+ description:
3567
+ '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).',
3568
+ example: 'USD',
3569
+ nullable: true
3570
+ }
3571
+ },
3572
+ required: ['baseCurrency']
3573
+ } as const;
3574
+
3575
+ export const $UserResponseDto = {
3576
+ type: 'object',
3577
+ properties: {
3578
+ id: {
3579
+ type: 'string',
3580
+ description: 'User ID'
3250
3581
  },
3251
- expectedDay: {
3252
- type: 'object',
3253
- description: 'Expected day of month'
3582
+ role: {
3583
+ type: 'string',
3584
+ description: 'Assigned user role'
3254
3585
  },
3255
- customIntervalDays: {
3256
- type: 'object',
3257
- description: 'Custom interval in days'
3586
+ permissions: {
3587
+ description: 'Permission strings',
3588
+ type: 'array',
3589
+ items: {
3590
+ type: 'string'
3591
+ }
3258
3592
  },
3259
- currency: {
3593
+ settings: {
3594
+ description: 'User settings',
3595
+ allOf: [
3596
+ {
3597
+ $ref: '#/components/schemas/UserSettingsResponseDto'
3598
+ }
3599
+ ]
3600
+ }
3601
+ },
3602
+ required: ['id', 'role', 'permissions', 'settings']
3603
+ } as const;
3604
+
3605
+ export const $SignupDto = {
3606
+ type: 'object',
3607
+ properties: {
3608
+ turnstileToken: {
3260
3609
  type: 'string',
3261
- description: 'Currency code'
3610
+ description:
3611
+ 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
3612
+ example: '0.abc123def456...'
3613
+ }
3614
+ }
3615
+ } as const;
3616
+
3617
+ export const $SignupResponseDto = {
3618
+ type: 'object',
3619
+ properties: {
3620
+ authToken: {
3621
+ type: 'string',
3622
+ description: 'JWT auth token',
3623
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
3262
3624
  },
3263
- matchPayeePattern: {
3264
- type: 'object',
3265
- description: 'Payee matching pattern'
3625
+ accessToken: {
3626
+ type: 'string',
3627
+ description: 'Auto-generated access token'
3266
3628
  },
3267
- matchAmountTolerance: {
3629
+ role: {
3630
+ type: 'string',
3631
+ description: 'Assigned user role',
3632
+ enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
3633
+ }
3634
+ },
3635
+ required: ['authToken', 'accessToken', 'role']
3636
+ } as const;
3637
+
3638
+ export const $UpdateUserSettingDto = {
3639
+ type: 'object',
3640
+ properties: {
3641
+ secId: {
3268
3642
  type: 'number',
3269
- description: 'Amount tolerance percentage'
3643
+ description: 'Security ID'
3270
3644
  },
3271
- defaultExpenseAccount: {
3272
- type: 'object',
3273
- description: 'Default expense account'
3645
+ annualInterestRate: {
3646
+ type: 'number',
3647
+ description: 'Annual interest rate',
3648
+ example: 0.05
3274
3649
  },
3275
- defaultPaymentAccount: {
3276
- type: 'object',
3277
- description: 'Default payment account'
3650
+ currency: {
3651
+ type: 'string',
3652
+ description: 'Currency code',
3653
+ example: 'USD'
3278
3654
  },
3279
- defaultPayee: {
3280
- type: 'object',
3281
- description: 'Default payee'
3655
+ baseCurrency: {
3656
+ type: 'string',
3657
+ description: 'Base currency code',
3658
+ example: 'USD'
3282
3659
  },
3283
- isActive: {
3284
- type: 'boolean',
3285
- description: 'Whether rule is active'
3660
+ benchmark: {
3661
+ type: 'string',
3662
+ description: 'Benchmark symbol',
3663
+ example: 'SPY'
3286
3664
  },
3287
- startDate: {
3665
+ colorScheme: {
3288
3666
  type: 'string',
3289
- description: 'Rule start date (YYYY-MM-DD)'
3667
+ description: 'Color scheme',
3668
+ enum: ['DARK', 'LIGHT']
3290
3669
  },
3291
- endDate: {
3292
- type: 'object',
3293
- description: 'Rule end date (YYYY-MM-DD)'
3670
+ dateRange: {
3671
+ type: 'string',
3672
+ description: 'Date range filter',
3673
+ example: '1y'
3294
3674
  },
3295
- autoCreate: {
3296
- type: 'boolean',
3297
- description: 'Auto-create transaction on expected date'
3675
+ emergencyFund: {
3676
+ type: 'number',
3677
+ description: 'Emergency fund amount',
3678
+ example: 10000
3298
3679
  },
3299
- lastOccurrence: {
3300
- type: 'object',
3301
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3680
+ 'filters.accounts': {
3681
+ description: 'Account filter IDs',
3682
+ type: 'array',
3683
+ items: {
3684
+ type: 'string'
3685
+ }
3302
3686
  },
3303
- totalCount: {
3304
- type: 'number',
3305
- description: 'Total matched transactions count'
3687
+ 'filters.assetClasses': {
3688
+ description: 'Asset class filters',
3689
+ type: 'array',
3690
+ items: {
3691
+ type: 'string'
3692
+ }
3306
3693
  },
3307
- createdAt: {
3308
- format: 'date-time',
3694
+ 'filters.dataSource': {
3309
3695
  type: 'string',
3310
- description: 'Created at timestamp'
3696
+ description: 'Data source filter'
3311
3697
  },
3312
- updatedAt: {
3313
- format: 'date-time',
3698
+ 'filters.symbol': {
3314
3699
  type: 'string',
3315
- description: 'Updated at timestamp'
3700
+ description: 'Symbol filter'
3316
3701
  },
3317
- pendingCount: {
3318
- type: 'number',
3319
- description: 'Number of pending expected transactions'
3702
+ 'filters.tags': {
3703
+ description: 'Tag filters',
3704
+ type: 'array',
3705
+ items: {
3706
+ type: 'string'
3707
+ }
3320
3708
  },
3321
- overdueCount: {
3322
- type: 'number',
3323
- description: 'Number of overdue expected transactions'
3709
+ isExperimentalFeatures: {
3710
+ type: 'boolean',
3711
+ description: 'Enable experimental features'
3324
3712
  },
3325
- nextExpectedDate: {
3326
- type: 'object',
3327
- description: 'Next expected date (YYYY-MM-DD)'
3713
+ isRestrictedView: {
3714
+ type: 'boolean',
3715
+ description: 'Enable restricted view mode'
3328
3716
  },
3329
- totalAmount: {
3330
- type: 'number',
3331
- description: 'Total amount of all matched transactions'
3717
+ language: {
3718
+ type: 'string',
3719
+ description: 'Language code',
3720
+ example: 'en'
3332
3721
  },
3333
- averageAmount: {
3334
- type: 'number',
3335
- description: 'Average amount per transaction'
3722
+ locale: {
3723
+ type: 'string',
3724
+ description: 'Locale code',
3725
+ example: 'en-US'
3336
3726
  },
3337
- transactionCount: {
3727
+ projectedTotalAmount: {
3338
3728
  type: 'number',
3339
- description: 'Number of matched transactions'
3729
+ description: 'Projected total amount',
3730
+ example: 1000000
3340
3731
  },
3341
- firstDate: {
3342
- type: 'object',
3343
- description: 'First matched transaction date (YYYY-MM-DD)'
3732
+ retirementDate: {
3733
+ type: 'string',
3734
+ description: 'Retirement date in ISO 8601 format',
3735
+ example: '2050-01-01'
3344
3736
  },
3345
- lastDate: {
3346
- type: 'object',
3347
- description: 'Last matched transaction date (YYYY-MM-DD)'
3348
- },
3349
- variance: {
3737
+ savingsRate: {
3350
3738
  type: 'number',
3351
- description: 'Amount variance (standard deviation squared)'
3739
+ description: 'Savings rate percentage',
3740
+ example: 0.2
3352
3741
  },
3353
- upcomingCount: {
3354
- type: 'number',
3355
- description: 'Number of upcoming expected transactions'
3742
+ viewMode: {
3743
+ type: 'string',
3744
+ description: 'View mode',
3745
+ enum: ['DEFAULT', 'ZEN']
3746
+ }
3747
+ }
3748
+ } as const;
3749
+
3750
+ export const $UpdatePropertyDto = {
3751
+ type: 'object',
3752
+ properties: {
3753
+ value: {
3754
+ type: 'string',
3755
+ description: 'Property value'
3356
3756
  }
3357
3757
  },
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
- ]
3758
+ required: ['value']
3380
3759
  } as const;
3381
3760
 
3382
- export const $UpdateRecurringRuleDto = {
3761
+ export const $CreateRecurringRuleDto = {
3383
3762
  type: 'object',
3384
3763
  properties: {
3385
3764
  name: {
3386
3765
  type: 'string',
3387
- description: 'Rule name',
3766
+ description: 'Rule name (unique per user)',
3388
3767
  maxLength: 100
3389
3768
  },
3390
3769
  icon: {
@@ -3407,7 +3786,7 @@ export const $UpdateRecurringRuleDto = {
3407
3786
  },
3408
3787
  expectedAmount: {
3409
3788
  type: 'number',
3410
- description: 'Expected amount',
3789
+ description: 'Expected amount (positive number)',
3411
3790
  minimum: 0
3412
3791
  },
3413
3792
  expectedDay: {
@@ -3418,7 +3797,7 @@ export const $UpdateRecurringRuleDto = {
3418
3797
  },
3419
3798
  customIntervalDays: {
3420
3799
  type: 'number',
3421
- description: 'Custom interval in days',
3800
+ description: 'Custom interval in days (required for CUSTOM frequency)',
3422
3801
  minimum: 1
3423
3802
  },
3424
3803
  currency: {
@@ -3428,118 +3807,136 @@ export const $UpdateRecurringRuleDto = {
3428
3807
  },
3429
3808
  matchPayeePattern: {
3430
3809
  type: 'string',
3431
- description: 'Payee matching pattern',
3810
+ description: 'Payee matching pattern (supports wildcards)',
3432
3811
  maxLength: 200
3433
3812
  },
3434
3813
  matchAmountTolerance: {
3435
3814
  type: 'number',
3436
3815
  description: 'Amount tolerance percentage (0-1)',
3816
+ default: 0.075,
3437
3817
  minimum: 0,
3438
3818
  maximum: 1
3439
3819
  },
3440
3820
  defaultExpenseAccount: {
3441
3821
  type: 'string',
3442
- description: 'Default expense account',
3822
+ description: 'Default expense account for auto-create',
3443
3823
  maxLength: 200
3444
3824
  },
3445
3825
  defaultPaymentAccount: {
3446
3826
  type: 'string',
3447
- description: 'Default payment account',
3827
+ description: 'Default payment account for auto-create',
3448
3828
  maxLength: 200
3449
3829
  },
3450
3830
  defaultPayee: {
3451
3831
  type: 'string',
3452
- description: 'Default payee',
3832
+ description: 'Default payee for auto-create',
3453
3833
  maxLength: 200
3454
3834
  },
3455
3835
  autoCreate: {
3456
3836
  type: 'boolean',
3457
- description: 'Auto-create transaction'
3837
+ description: 'Auto-create transaction when expected date arrives',
3838
+ default: false
3458
3839
  },
3459
- isActive: {
3460
- type: 'boolean',
3461
- description: 'Rule active status'
3840
+ startDate: {
3841
+ type: 'string',
3842
+ description: 'Rule start date (ISO format)'
3462
3843
  },
3463
3844
  endDate: {
3464
3845
  type: 'string',
3465
3846
  description: 'Rule end date (ISO format)'
3466
3847
  }
3467
- }
3848
+ },
3849
+ required: [
3850
+ 'name',
3851
+ 'frequency',
3852
+ 'expectedAmount',
3853
+ 'matchAmountTolerance',
3854
+ 'autoCreate'
3855
+ ]
3468
3856
  } as const;
3469
3857
 
3470
- export const $ExpectedTransactionRuleDto = {
3858
+ export const $RecurringRuleResponseDto = {
3471
3859
  type: 'object',
3472
3860
  properties: {
3861
+ id: {
3862
+ type: 'string',
3863
+ description: 'Rule ID'
3864
+ },
3865
+ userId: {
3866
+ type: 'string',
3867
+ description: 'User ID'
3868
+ },
3473
3869
  name: {
3474
3870
  type: 'string',
3475
3871
  description: 'Rule name'
3476
3872
  },
3477
3873
  icon: {
3478
- type: 'object',
3479
- description: 'Rule icon'
3874
+ type: 'string',
3875
+ description: 'Icon emoji'
3480
3876
  },
3481
3877
  frequency: {
3482
3878
  type: 'string',
3483
- description: 'Rule frequency'
3879
+ description: 'Recurring frequency'
3880
+ },
3881
+ expectedAmount: {
3882
+ type: 'number',
3883
+ description: 'Expected amount'
3884
+ },
3885
+ expectedDay: {
3886
+ type: 'number',
3887
+ description: 'Expected day of month'
3888
+ },
3889
+ customIntervalDays: {
3890
+ type: 'number',
3891
+ description: 'Custom interval in days'
3484
3892
  },
3485
3893
  currency: {
3486
3894
  type: 'string',
3487
3895
  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
3896
  },
3500
- userId: {
3897
+ matchPayeePattern: {
3501
3898
  type: 'string',
3502
- description: 'User ID'
3899
+ description: 'Payee matching pattern'
3503
3900
  },
3504
- ruleId: {
3505
- type: 'string',
3506
- description: 'Associated rule ID'
3901
+ matchAmountTolerance: {
3902
+ type: 'number',
3903
+ description: 'Amount tolerance percentage'
3507
3904
  },
3508
- expectedDate: {
3905
+ defaultExpenseAccount: {
3509
3906
  type: 'string',
3510
- description: 'Expected date (YYYY-MM-DD)'
3907
+ description: 'Default expense account'
3511
3908
  },
3512
- expectedAmount: {
3513
- type: 'number',
3514
- description: 'Expected amount'
3909
+ defaultPaymentAccount: {
3910
+ type: 'string',
3911
+ description: 'Default payment account'
3515
3912
  },
3516
- status: {
3913
+ defaultPayee: {
3517
3914
  type: 'string',
3518
- description: 'Status (PENDING, COMPLETED, SKIPPED)'
3915
+ description: 'Default payee'
3519
3916
  },
3520
- matchedTransactionId: {
3521
- type: 'object',
3522
- description: 'Matched transaction ID'
3917
+ isActive: {
3918
+ type: 'boolean',
3919
+ description: 'Whether rule is active'
3523
3920
  },
3524
- matchedAt: {
3525
- type: 'object',
3526
- description: 'Match timestamp (ISO 8601)'
3921
+ startDate: {
3922
+ type: 'string',
3923
+ description: 'Rule start date (YYYY-MM-DD)'
3527
3924
  },
3528
- matchConfidence: {
3529
- type: 'object',
3530
- description: 'Match confidence score (0-1)'
3925
+ endDate: {
3926
+ type: 'string',
3927
+ description: 'Rule end date (YYYY-MM-DD)'
3531
3928
  },
3532
- isOverdue: {
3929
+ autoCreate: {
3533
3930
  type: 'boolean',
3534
- description: 'Whether this expected transaction is overdue'
3931
+ description: 'Auto-create transaction on expected date'
3535
3932
  },
3536
- rule: {
3537
- description: 'Rule information',
3538
- allOf: [
3539
- {
3540
- $ref: '#/components/schemas/ExpectedTransactionRuleDto'
3541
- }
3542
- ]
3933
+ lastOccurrence: {
3934
+ type: 'string',
3935
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
3936
+ },
3937
+ totalCount: {
3938
+ type: 'number',
3939
+ description: 'Total matched transactions count'
3543
3940
  },
3544
3941
  createdAt: {
3545
3942
  format: 'date-time',
@@ -3555,647 +3952,581 @@ export const $ExpectedTransactionResponseDto = {
3555
3952
  required: [
3556
3953
  'id',
3557
3954
  'userId',
3558
- 'ruleId',
3559
- 'expectedDate',
3955
+ 'name',
3956
+ 'frequency',
3560
3957
  'expectedAmount',
3561
- 'status',
3562
- 'isOverdue',
3563
- 'rule',
3958
+ 'currency',
3959
+ 'matchAmountTolerance',
3960
+ 'isActive',
3961
+ 'startDate',
3962
+ 'autoCreate',
3963
+ 'totalCount',
3564
3964
  'createdAt',
3565
3965
  'updatedAt'
3566
3966
  ]
3567
3967
  } as const;
3568
3968
 
3569
- export const $ExpectedTransactionListResponseDto = {
3969
+ export const $CreateRuleFromTransactionDto = {
3570
3970
  type: 'object',
3571
3971
  properties: {
3572
- items: {
3573
- type: 'array',
3574
- items: {
3575
- $ref: '#/components/schemas/ExpectedTransactionResponseDto'
3576
- }
3972
+ frequency: {
3973
+ type: 'string',
3974
+ description: 'Recurring frequency',
3975
+ enum: [
3976
+ 'WEEKLY',
3977
+ 'BIWEEKLY',
3978
+ 'MONTHLY',
3979
+ 'BIMONTHLY',
3980
+ 'QUARTERLY',
3981
+ 'YEARLY',
3982
+ 'CUSTOM'
3983
+ ],
3984
+ example: 'MONTHLY'
3577
3985
  },
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: {
3986
+ name: {
3590
3987
  type: 'string',
3591
- description: 'Transaction ID to match with'
3988
+ description: 'Optional name override (default: transaction payee)',
3989
+ maxLength: 100
3990
+ },
3991
+ icon: {
3992
+ type: 'string',
3993
+ description: 'Optional icon emoji',
3994
+ maxLength: 10
3592
3995
  }
3593
3996
  },
3594
- required: ['transactionId']
3997
+ required: ['frequency']
3595
3998
  } as const;
3596
3999
 
3597
- export const $EnterNowDto = {
4000
+ export const $RecurringRuleWithStatsResponseDto = {
3598
4001
  type: 'object',
3599
4002
  properties: {
3600
- expenseAccount: {
4003
+ id: {
3601
4004
  type: 'string',
3602
- description:
3603
- 'Override expense account (uses rule default if not provided)',
3604
- maxLength: 200
4005
+ description: 'Rule ID'
3605
4006
  },
3606
- paymentAccount: {
4007
+ userId: {
3607
4008
  type: 'string',
3608
- description:
3609
- 'Override payment account (uses rule default if not provided)',
3610
- maxLength: 200
4009
+ description: 'User ID'
3611
4010
  },
3612
- amount: {
3613
- type: 'number',
3614
- description: 'Override amount (uses expected amount if not provided)',
3615
- minimum: 0
4011
+ name: {
4012
+ type: 'string',
4013
+ description: 'Rule name'
3616
4014
  },
3617
- payee: {
4015
+ icon: {
3618
4016
  type: 'string',
3619
- description: 'Override payee (uses rule default if not provided)',
3620
- maxLength: 200
4017
+ description: 'Icon emoji'
3621
4018
  },
3622
- narration: {
4019
+ frequency: {
3623
4020
  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: {
4021
+ description: 'Recurring frequency'
4022
+ },
4023
+ expectedAmount: {
4024
+ type: 'number',
4025
+ description: 'Expected amount'
4026
+ },
4027
+ expectedDay: {
4028
+ type: 'number',
4029
+ description: 'Expected day of month'
4030
+ },
4031
+ customIntervalDays: {
4032
+ type: 'number',
4033
+ description: 'Custom interval in days'
4034
+ },
4035
+ currency: {
3634
4036
  type: 'string',
3635
- description: 'Rule name',
3636
- example: 'Rent'
4037
+ description: 'Currency code'
3637
4038
  },
3638
- ruleId: {
4039
+ matchPayeePattern: {
3639
4040
  type: 'string',
3640
- description: 'Rule ID',
3641
- example: 'clx123...'
4041
+ description: 'Payee matching pattern'
3642
4042
  },
3643
- amount: {
4043
+ matchAmountTolerance: {
3644
4044
  type: 'number',
3645
- description: 'Expected amount',
3646
- example: 3000
4045
+ description: 'Amount tolerance percentage'
3647
4046
  },
3648
- date: {
4047
+ defaultExpenseAccount: {
3649
4048
  type: 'string',
3650
- description: 'Expected date (YYYY-MM-DD)',
3651
- example: '2024-04-01'
4049
+ description: 'Default expense account'
3652
4050
  },
3653
- icon: {
4051
+ defaultPaymentAccount: {
3654
4052
  type: 'string',
3655
- description: 'Rule icon emoji',
3656
- example: '🏠',
3657
- nullable: true
4053
+ description: 'Default payment account'
3658
4054
  },
3659
- currency: {
4055
+ defaultPayee: {
3660
4056
  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: {
4057
+ description: 'Default payee'
4058
+ },
4059
+ isActive: {
4060
+ type: 'boolean',
4061
+ description: 'Whether rule is active'
4062
+ },
4063
+ startDate: {
3672
4064
  type: 'string',
3673
- description: 'Month (YYYY-MM)',
3674
- example: '2024-04'
4065
+ description: 'Rule start date (YYYY-MM-DD)'
3675
4066
  },
3676
- expectedOutflow: {
4067
+ endDate: {
4068
+ type: 'string',
4069
+ description: 'Rule end date (YYYY-MM-DD)'
4070
+ },
4071
+ autoCreate: {
4072
+ type: 'boolean',
4073
+ description: 'Auto-create transaction on expected date'
4074
+ },
4075
+ lastOccurrence: {
4076
+ type: 'string',
4077
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
4078
+ },
4079
+ totalCount: {
3677
4080
  type: 'number',
3678
- description: 'Total expected outflow for the month',
3679
- example: 8500
4081
+ description: 'Total matched transactions count'
3680
4082
  },
3681
- itemCount: {
4083
+ createdAt: {
4084
+ format: 'date-time',
4085
+ type: 'string',
4086
+ description: 'Created at timestamp'
4087
+ },
4088
+ updatedAt: {
4089
+ format: 'date-time',
4090
+ type: 'string',
4091
+ description: 'Updated at timestamp'
4092
+ },
4093
+ pendingCount: {
3682
4094
  type: 'number',
3683
- description: 'Number of expected transactions',
3684
- example: 3
4095
+ description: 'Number of pending expected transactions'
3685
4096
  },
3686
- byCurrency: {
3687
- type: 'object',
3688
- description: 'Breakdown by currency',
3689
- example: {
3690
- CNY: 8500,
3691
- USD: 100
3692
- }
4097
+ overdueCount: {
4098
+ type: 'number',
4099
+ description: 'Number of overdue expected transactions'
3693
4100
  },
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
- }
4101
+ nextExpectedDate: {
4102
+ type: 'string',
4103
+ description: 'Next expected date (YYYY-MM-DD)'
3714
4104
  },
3715
- totalOutflow: {
4105
+ totalAmount: {
3716
4106
  type: 'number',
3717
- description: 'Total expected outflow across all months',
3718
- example: 25500
4107
+ description: 'Total amount of all matched transactions'
3719
4108
  },
3720
- totalByCurrency: {
3721
- type: 'object',
3722
- description: 'Total by currency across all months',
3723
- example: {
3724
- CNY: 25500,
3725
- USD: 300
3726
- }
4109
+ averageAmount: {
4110
+ type: 'number',
4111
+ description: 'Average amount per transaction'
3727
4112
  },
3728
- rulesCount: {
4113
+ transactionCount: {
3729
4114
  type: 'number',
3730
- description: 'Number of active recurring rules included',
3731
- example: 5
4115
+ description: 'Number of matched transactions'
3732
4116
  },
3733
- periodStart: {
4117
+ firstDate: {
3734
4118
  type: 'string',
3735
- description: 'Forecast period start date',
3736
- example: '2024-04-01'
4119
+ description: 'First matched transaction date (YYYY-MM-DD)'
3737
4120
  },
3738
- periodEnd: {
4121
+ lastDate: {
3739
4122
  type: 'string',
3740
- description: 'Forecast period end date',
3741
- example: '2024-06-30'
4123
+ description: 'Last matched transaction date (YYYY-MM-DD)'
4124
+ },
4125
+ variance: {
4126
+ type: 'number',
4127
+ description: 'Amount variance (standard deviation squared)'
4128
+ },
4129
+ upcomingCount: {
4130
+ type: 'number',
4131
+ description: 'Number of upcoming expected transactions'
3742
4132
  }
3743
4133
  },
3744
4134
  required: [
3745
- 'forecast',
3746
- 'totalOutflow',
3747
- 'totalByCurrency',
3748
- 'rulesCount',
3749
- 'periodStart',
3750
- 'periodEnd'
4135
+ 'id',
4136
+ 'userId',
4137
+ 'name',
4138
+ 'frequency',
4139
+ 'expectedAmount',
4140
+ 'currency',
4141
+ 'matchAmountTolerance',
4142
+ 'isActive',
4143
+ 'startDate',
4144
+ 'autoCreate',
4145
+ 'totalCount',
4146
+ 'createdAt',
4147
+ 'updatedAt',
4148
+ 'pendingCount',
4149
+ 'overdueCount',
4150
+ 'totalAmount',
4151
+ 'averageAmount',
4152
+ 'transactionCount',
4153
+ 'variance',
4154
+ 'upcomingCount'
3751
4155
  ]
3752
4156
  } as const;
3753
4157
 
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 = {
4158
+ export const $UpdateRecurringRuleDto = {
3772
4159
  type: 'object',
3773
4160
  properties: {
3774
- date: {
4161
+ name: {
3775
4162
  type: 'string',
3776
- description: 'Date in YYYY-MM-DD format',
3777
- example: '2024-06-15'
4163
+ description: 'Rule name (unique per user)',
4164
+ maxLength: 100
3778
4165
  },
3779
- value: {
4166
+ icon: {
3780
4167
  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'
4168
+ description: 'Icon emoji',
4169
+ maxLength: 10
3788
4170
  },
3789
- assets: {
4171
+ frequency: {
3790
4172
  type: 'string',
3791
- description: 'Total assets at this date (in base currency)',
3792
- example: '494338.00'
4173
+ description: 'Recurring frequency',
4174
+ enum: [
4175
+ 'WEEKLY',
4176
+ 'BIWEEKLY',
4177
+ 'MONTHLY',
4178
+ 'BIMONTHLY',
4179
+ 'QUARTERLY',
4180
+ 'YEARLY',
4181
+ 'CUSTOM'
4182
+ ]
3793
4183
  },
3794
- liabilities: {
3795
- type: 'string',
3796
- description: 'Total liabilities at this date (in base currency)',
3797
- example: '310098.00'
4184
+ expectedAmount: {
4185
+ type: 'number',
4186
+ description: 'Expected amount (positive number)',
4187
+ minimum: 0
3798
4188
  },
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: {
4189
+ expectedDay: {
4190
+ type: 'number',
4191
+ description: 'Expected day of month (1-31)',
4192
+ minimum: 1,
4193
+ maximum: 31
4194
+ },
4195
+ currency: {
3814
4196
  type: 'string',
3815
- description: 'Value at start of period',
3816
- example: '450000.00'
4197
+ description: 'Currency code',
4198
+ maxLength: 10
3817
4199
  },
3818
- endValue: {
4200
+ matchPayeePattern: {
3819
4201
  type: 'string',
3820
- description: 'Value at end of period',
3821
- example: '500000.00'
4202
+ description: 'Payee matching pattern (supports wildcards)',
4203
+ maxLength: 200
3822
4204
  },
3823
- totalChange: {
4205
+ matchAmountTolerance: {
4206
+ type: 'number',
4207
+ description: 'Amount tolerance percentage (0-1)',
4208
+ default: 0.075,
4209
+ minimum: 0,
4210
+ maximum: 1
4211
+ },
4212
+ defaultExpenseAccount: {
3824
4213
  type: 'string',
3825
- description: 'Total change over period',
3826
- example: '50000.00'
4214
+ description: 'Default expense account for auto-create',
4215
+ maxLength: 200
3827
4216
  },
3828
- totalChangePercentage: {
4217
+ defaultPaymentAccount: {
3829
4218
  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: {
4219
+ description: 'Default payment account for auto-create',
4220
+ maxLength: 200
4221
+ },
4222
+ defaultPayee: {
3841
4223
  type: 'string',
3842
- description: 'Date in YYYY-MM-DD format',
3843
- example: '2024-06-15'
4224
+ description: 'Default payee for auto-create',
4225
+ maxLength: 200
3844
4226
  },
3845
- byCurrency: {
3846
- description: 'Balances by currency',
3847
- type: 'array',
3848
- items: {
3849
- $ref: '#/components/schemas/CurrencyBalanceDto'
3850
- }
4227
+ autoCreate: {
4228
+ type: 'boolean',
4229
+ description: 'Auto-create transaction when expected date arrives',
4230
+ default: false
4231
+ },
4232
+ endDate: {
4233
+ type: 'string',
4234
+ description: 'Rule end date (ISO format)'
4235
+ },
4236
+ customIntervalDays: {
4237
+ type: 'number',
4238
+ description: 'Custom interval in days',
4239
+ minimum: 1
4240
+ },
4241
+ isActive: {
4242
+ type: 'boolean',
4243
+ description: 'Rule active status'
3851
4244
  }
3852
- },
3853
- required: ['date', 'byCurrency']
4245
+ }
3854
4246
  } as const;
3855
4247
 
3856
- export const $PortfolioTrendsResponseDto = {
4248
+ export const $ExpectedTransactionRuleDto = {
3857
4249
  type: 'object',
3858
4250
  properties: {
3859
- series: {
3860
- description: 'Time series data points',
3861
- type: 'array',
3862
- items: {
3863
- $ref: '#/components/schemas/TimeSeriesPointDto'
3864
- }
3865
- },
3866
- summary: {
3867
- description: 'Period summary',
3868
- allOf: [
3869
- {
3870
- $ref: '#/components/schemas/TrendSummaryDto'
3871
- }
3872
- ]
4251
+ name: {
4252
+ type: 'string',
4253
+ description: 'Rule name'
3873
4254
  },
3874
- period: {
4255
+ icon: {
3875
4256
  type: 'string',
3876
- description: 'Period requested',
3877
- example: '6m'
4257
+ description: 'Rule icon'
3878
4258
  },
3879
- granularity: {
4259
+ frequency: {
3880
4260
  type: 'string',
3881
- description: 'Data granularity',
3882
- example: 'month'
4261
+ description: 'Rule frequency'
3883
4262
  },
3884
4263
  currency: {
3885
4264
  type: 'string',
3886
- description: 'Base currency for converted values',
3887
- example: 'CNY'
3888
- },
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
- }
3896
- },
3897
- warnings: {
3898
- description: 'Exchange rate warnings',
3899
- type: 'array',
3900
- items: {
3901
- $ref: '#/components/schemas/ExchangeRateWarningDto'
3902
- }
4265
+ description: 'Currency code'
3903
4266
  }
3904
4267
  },
3905
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4268
+ required: ['name', 'frequency', 'currency']
3906
4269
  } as const;
3907
4270
 
3908
- export const $CashFlowPointDto = {
4271
+ export const $ExpectedTransactionResponseDto = {
3909
4272
  type: 'object',
3910
4273
  properties: {
3911
- month: {
4274
+ id: {
3912
4275
  type: 'string',
3913
- description: 'Month key (YYYY-MM)',
3914
- example: '2024-03'
4276
+ description: 'Expected transaction ID'
3915
4277
  },
3916
- income: {
4278
+ userId: {
3917
4279
  type: 'string',
3918
- description: 'Income in base currency (absolute, converted)',
3919
- example: '10000.00'
4280
+ description: 'User ID'
3920
4281
  },
3921
- expense: {
4282
+ ruleId: {
3922
4283
  type: 'string',
3923
- description: 'Expense in base currency (absolute, converted)',
3924
- example: '5000.00'
4284
+ description: 'Associated rule ID'
3925
4285
  },
3926
- netSavings: {
3927
- type: 'string',
3928
- description: 'netSavings = income − expense (savings positive)',
3929
- example: '5000.00'
3930
- }
3931
- },
3932
- required: ['month', 'income', 'expense', 'netSavings']
3933
- } as const;
3934
-
3935
- export const $CashFlowTrendSummaryDto = {
3936
- type: 'object',
3937
- properties: {
3938
- totalIncome: {
4286
+ expectedDate: {
3939
4287
  type: 'string',
3940
- description: 'Total income across the period',
3941
- example: '60000.00'
4288
+ description: 'Expected date (YYYY-MM-DD)'
3942
4289
  },
3943
- totalExpense: {
4290
+ expectedAmount: {
4291
+ type: 'number',
4292
+ description: 'Expected amount'
4293
+ },
4294
+ status: {
3944
4295
  type: 'string',
3945
- description: 'Total expense across the period',
3946
- example: '30000.00'
4296
+ description: 'Status (PENDING, COMPLETED, SKIPPED)'
3947
4297
  },
3948
- totalNetSavings: {
4298
+ matchedTransactionId: {
3949
4299
  type: 'string',
3950
- description: 'income − expense across the period',
3951
- example: '30000.00'
4300
+ description: 'Matched transaction ID'
3952
4301
  },
3953
- averageMonthlyNetSavings: {
4302
+ matchedAt: {
3954
4303
  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
- }
4304
+ description: 'Match timestamp (ISO 8601)'
3978
4305
  },
3979
- summary: {
3980
- description: 'Period totals',
4306
+ matchConfidence: {
4307
+ type: 'number',
4308
+ description: 'Match confidence score (0-1)'
4309
+ },
4310
+ isOverdue: {
4311
+ type: 'boolean',
4312
+ description: 'Whether this expected transaction is overdue'
4313
+ },
4314
+ rule: {
4315
+ description: 'Rule information',
3981
4316
  allOf: [
3982
4317
  {
3983
- $ref: '#/components/schemas/CashFlowTrendSummaryDto'
4318
+ $ref: '#/components/schemas/ExpectedTransactionRuleDto'
3984
4319
  }
3985
4320
  ]
3986
4321
  },
3987
- period: {
3988
- type: 'string',
3989
- description: 'Period requested',
3990
- example: '6m'
3991
- },
3992
- granularity: {
4322
+ createdAt: {
4323
+ format: 'date-time',
3993
4324
  type: 'string',
3994
- description: 'Data granularity (v1 returns month buckets)',
3995
- example: 'month'
4325
+ description: 'Created at timestamp'
3996
4326
  },
3997
- currency: {
4327
+ updatedAt: {
4328
+ format: 'date-time',
3998
4329
  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
- }
4330
+ description: 'Updated at timestamp'
4008
4331
  }
4009
4332
  },
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: {}
4333
+ required: [
4334
+ 'id',
4335
+ 'userId',
4336
+ 'ruleId',
4337
+ 'expectedDate',
4338
+ 'expectedAmount',
4339
+ 'status',
4340
+ 'isOverdue',
4341
+ 'rule',
4342
+ 'createdAt',
4343
+ 'updatedAt'
4344
+ ]
4031
4345
  } as const;
4032
4346
 
4033
- export const $DeleteOwnUserDto = {
4347
+ export const $ExpectedTransactionListResponseDto = {
4034
4348
  type: 'object',
4035
4349
  properties: {
4036
- accessToken: {
4037
- type: 'string',
4038
- description: 'Access token for user verification',
4039
- example: 'abc123xyz'
4350
+ items: {
4351
+ type: 'array',
4352
+ items: {
4353
+ $ref: '#/components/schemas/ExpectedTransactionResponseDto'
4354
+ }
4355
+ },
4356
+ total: {
4357
+ type: 'number',
4358
+ description: 'Total count'
4040
4359
  }
4041
4360
  },
4042
- required: ['accessToken']
4361
+ required: ['items', 'total']
4043
4362
  } as const;
4044
4363
 
4045
- export const $SignupDto = {
4364
+ export const $ConfirmMatchDto = {
4046
4365
  type: 'object',
4047
4366
  properties: {
4048
- turnstileToken: {
4367
+ transactionId: {
4049
4368
  type: 'string',
4050
- description:
4051
- 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4052
- example: '0.abc123def456...'
4369
+ description: 'Transaction ID to match with'
4053
4370
  }
4054
- }
4371
+ },
4372
+ required: ['transactionId']
4055
4373
  } as const;
4056
4374
 
4057
- export const $SignupResponseDto = {
4375
+ export const $EnterNowDto = {
4058
4376
  type: 'object',
4059
4377
  properties: {
4060
- authToken: {
4378
+ expenseAccount: {
4061
4379
  type: 'string',
4062
- description: 'JWT auth token',
4063
- example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
4380
+ description:
4381
+ 'Override expense account (uses rule default if not provided)',
4382
+ maxLength: 200
4064
4383
  },
4065
- accessToken: {
4384
+ paymentAccount: {
4066
4385
  type: 'string',
4067
- description: 'Auto-generated access token'
4386
+ description:
4387
+ 'Override payment account (uses rule default if not provided)',
4388
+ maxLength: 200
4068
4389
  },
4069
- role: {
4390
+ amount: {
4391
+ type: 'number',
4392
+ description: 'Override amount (uses expected amount if not provided)',
4393
+ minimum: 0
4394
+ },
4395
+ payee: {
4070
4396
  type: 'string',
4071
- description: 'Assigned user role',
4072
- enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
4397
+ description: 'Override payee (uses rule default if not provided)',
4398
+ maxLength: 200
4399
+ },
4400
+ narration: {
4401
+ type: 'string',
4402
+ description: 'Optional narration',
4403
+ maxLength: 500
4073
4404
  }
4074
- },
4075
- required: ['authToken', 'accessToken', 'role']
4405
+ }
4076
4406
  } as const;
4077
4407
 
4078
- export const $UpdateUserSettingDto = {
4408
+ export const $ForecastItemDto = {
4079
4409
  type: 'object',
4080
4410
  properties: {
4081
- secId: {
4082
- type: 'number',
4083
- description: 'Security ID'
4411
+ rule: {
4412
+ type: 'string',
4413
+ description: 'Rule name',
4414
+ example: 'Rent'
4084
4415
  },
4085
- annualInterestRate: {
4416
+ ruleId: {
4417
+ type: 'string',
4418
+ description: 'Rule ID',
4419
+ example: 'clx123...'
4420
+ },
4421
+ amount: {
4086
4422
  type: 'number',
4087
- description: 'Annual interest rate',
4088
- example: 0.05
4423
+ description: 'Expected amount',
4424
+ example: 3000
4089
4425
  },
4090
- currency: {
4426
+ date: {
4091
4427
  type: 'string',
4092
- description: 'Currency code',
4093
- example: 'USD'
4428
+ description: 'Expected date (YYYY-MM-DD)',
4429
+ example: '2024-04-01'
4094
4430
  },
4095
- baseCurrency: {
4431
+ icon: {
4096
4432
  type: 'string',
4097
- description: 'Base currency code',
4098
- example: 'USD'
4433
+ description: 'Rule icon emoji',
4434
+ example: '🏠',
4435
+ nullable: true
4099
4436
  },
4100
- benchmark: {
4437
+ currency: {
4101
4438
  type: 'string',
4102
- description: 'Benchmark symbol',
4103
- example: 'SPY'
4104
- },
4105
- colorScheme: {
4439
+ description: 'Currency code',
4440
+ example: 'CNY'
4441
+ }
4442
+ },
4443
+ required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
4444
+ } as const;
4445
+
4446
+ export const $MonthlyForecastDto = {
4447
+ type: 'object',
4448
+ properties: {
4449
+ month: {
4106
4450
  type: 'string',
4107
- description: 'Color scheme',
4108
- enum: ['DARK', 'LIGHT']
4451
+ description: 'Month (YYYY-MM)',
4452
+ example: '2024-04'
4109
4453
  },
4110
- dateRange: {
4111
- type: 'string',
4112
- description: 'Date range filter',
4113
- example: '1y'
4454
+ expectedOutflow: {
4455
+ type: 'number',
4456
+ description: 'Total expected outflow for the month',
4457
+ example: 8500
4114
4458
  },
4115
- emergencyFund: {
4459
+ itemCount: {
4116
4460
  type: 'number',
4117
- description: 'Emergency fund amount',
4118
- example: 10000
4461
+ description: 'Number of expected transactions',
4462
+ example: 3
4119
4463
  },
4120
- 'filters.accounts': {
4121
- description: 'Account filter IDs',
4122
- type: 'array',
4123
- items: {
4124
- type: 'string'
4464
+ byCurrency: {
4465
+ type: 'object',
4466
+ description: 'Breakdown by currency',
4467
+ example: {
4468
+ CNY: 8500,
4469
+ USD: 100
4125
4470
  }
4126
4471
  },
4127
- 'filters.assetClasses': {
4128
- description: 'Asset class filters',
4472
+ items: {
4473
+ description: 'Individual forecast items',
4129
4474
  type: 'array',
4130
4475
  items: {
4131
- type: 'string'
4476
+ $ref: '#/components/schemas/ForecastItemDto'
4132
4477
  }
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',
4478
+ }
4479
+ },
4480
+ required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
4481
+ } as const;
4482
+
4483
+ export const $ForecastResponseDto = {
4484
+ type: 'object',
4485
+ properties: {
4486
+ forecast: {
4487
+ description: 'Monthly forecast data',
4144
4488
  type: 'array',
4145
4489
  items: {
4146
- type: 'string'
4490
+ $ref: '#/components/schemas/MonthlyForecastDto'
4147
4491
  }
4148
4492
  },
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: {
4493
+ totalOutflow: {
4168
4494
  type: 'number',
4169
- description: 'Projected total amount',
4170
- example: 1000000
4495
+ description: 'Total expected outflow across all months',
4496
+ example: 25500
4171
4497
  },
4172
- retirementDate: {
4173
- type: 'string',
4174
- description: 'Retirement date in ISO 8601 format',
4175
- example: '2050-01-01'
4498
+ totalByCurrency: {
4499
+ type: 'object',
4500
+ description: 'Total by currency across all months',
4501
+ example: {
4502
+ CNY: 25500,
4503
+ USD: 300
4504
+ }
4176
4505
  },
4177
- savingsRate: {
4506
+ rulesCount: {
4178
4507
  type: 'number',
4179
- description: 'Savings rate percentage',
4180
- example: 0.2
4508
+ description: 'Number of active recurring rules included',
4509
+ example: 5
4181
4510
  },
4182
- viewMode: {
4511
+ periodStart: {
4183
4512
  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: {
4513
+ description: 'Forecast period start date',
4514
+ example: '2024-04-01'
4515
+ },
4516
+ periodEnd: {
4194
4517
  type: 'string',
4195
- description: 'Property value'
4518
+ description: 'Forecast period end date',
4519
+ example: '2024-06-30'
4196
4520
  }
4197
4521
  },
4198
- required: ['value']
4522
+ required: [
4523
+ 'forecast',
4524
+ 'totalOutflow',
4525
+ 'totalByCurrency',
4526
+ 'rulesCount',
4527
+ 'periodStart',
4528
+ 'periodEnd'
4529
+ ]
4199
4530
  } as const;
4200
4531
 
4201
4532
  export const $CreateTransactionRuleDto = {
@@ -4757,7 +5088,8 @@ export const $UpdateTransactionRuleDto = {
4757
5088
  },
4758
5089
  matchLogic: {
4759
5090
  type: 'string',
4760
- enum: ['OR', 'AND']
5091
+ enum: ['OR', 'AND'],
5092
+ default: 'OR'
4761
5093
  },
4762
5094
  amountMin: {
4763
5095
  type: 'number',
@@ -4771,13 +5103,10 @@ export const $UpdateTransactionRuleDto = {
4771
5103
  },
4772
5104
  priority: {
4773
5105
  type: 'number',
5106
+ default: 50,
4774
5107
  minimum: 0,
4775
5108
  maximum: 1000
4776
5109
  },
4777
- enabled: {
4778
- type: 'boolean',
4779
- description: 'Enable or disable the rule'
4780
- },
4781
5110
  additionalTags: {
4782
5111
  items: {
4783
5112
  type: 'array'
@@ -4787,6 +5116,10 @@ export const $UpdateTransactionRuleDto = {
4787
5116
  },
4788
5117
  additionalMetadata: {
4789
5118
  type: 'object'
5119
+ },
5120
+ enabled: {
5121
+ type: 'boolean',
5122
+ description: 'Enable or disable the rule'
4790
5123
  }
4791
5124
  }
4792
5125
  } as const;
@@ -4847,6 +5180,69 @@ export const $TestRuleResponseDto = {
4847
5180
  required: ['ruleId', 'matches', 'confidence', 'matchDetails']
4848
5181
  } as const;
4849
5182
 
5183
+ export const $CategoryCatalogEntryDto = {
5184
+ type: 'object',
5185
+ properties: {
5186
+ slug: {
5187
+ type: 'string',
5188
+ description: 'Category slug (single source-of-truth)',
5189
+ example: 'food'
5190
+ },
5191
+ scenario: {
5192
+ type: 'string',
5193
+ description: 'Display scenario group (maps to frontend picker _scenario)',
5194
+ enum: [
5195
+ 'expense',
5196
+ 'income',
5197
+ 'investment',
5198
+ 'banking',
5199
+ 'transfer',
5200
+ 'payment'
5201
+ ],
5202
+ example: 'expense'
5203
+ },
5204
+ icon: {
5205
+ type: 'string',
5206
+ description: 'Lucide icon name',
5207
+ example: 'utensils'
5208
+ },
5209
+ regions: {
5210
+ description: "Applicable regions ('*' = all, 'cn' = CN-only)",
5211
+ example: ['*'],
5212
+ type: 'array',
5213
+ items: {
5214
+ type: 'string'
5215
+ }
5216
+ }
5217
+ },
5218
+ required: ['slug', 'scenario', 'icon', 'regions']
5219
+ } as const;
5220
+
5221
+ export const $CategoryCatalogListResponseDto = {
5222
+ type: 'object',
5223
+ properties: {
5224
+ items: {
5225
+ description: 'Category entries (region-scoped, query-filtered)',
5226
+ type: 'array',
5227
+ items: {
5228
+ $ref: '#/components/schemas/CategoryCatalogEntryDto'
5229
+ }
5230
+ },
5231
+ total: {
5232
+ type: 'number',
5233
+ description:
5234
+ 'Total category entries for the region (before query filtering)',
5235
+ example: 30
5236
+ },
5237
+ region: {
5238
+ type: 'string',
5239
+ description: 'Region code',
5240
+ example: 'cn'
5241
+ }
5242
+ },
5243
+ required: ['items', 'total', 'region']
5244
+ } as const;
5245
+
4850
5246
  export const $CreateBeanEventDto = {
4851
5247
  type: 'object',
4852
5248
  properties: {
@@ -5589,7 +5985,8 @@ export const $UpdateMapperDefaultsDto = {
5589
5985
  type: 'string',
5590
5986
  description: 'Source account for transactions (Beancount format)',
5591
5987
  example: 'Assets:CN:Alipay:Balance',
5592
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
5988
+ pattern:
5989
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5593
5990
  },
5594
5991
  currency: {
5595
5992
  type: 'string',
@@ -5603,13 +6000,15 @@ export const $UpdateMapperDefaultsDto = {
5603
6000
  type: 'string',
5604
6001
  description: 'Default expense account (optional)',
5605
6002
  example: 'Expenses:Unknown',
5606
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6003
+ pattern:
6004
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5607
6005
  },
5608
6006
  incomeAccount: {
5609
6007
  type: 'string',
5610
6008
  description: 'Default income account (optional)',
5611
6009
  example: 'Income:Unknown',
5612
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6010
+ pattern:
6011
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5613
6012
  },
5614
6013
  methodAccountMapping: {
5615
6014
  type: 'object',
@@ -5666,12 +6065,14 @@ export const $ProviderSyncConfigDto = {
5666
6065
  },
5667
6066
  defaultExpenseAccount: {
5668
6067
  type: 'string',
5669
- description: 'Default expense account for the second posting',
6068
+ description:
6069
+ 'Default expense account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5670
6070
  example: 'Expenses:Unknown'
5671
6071
  },
5672
6072
  defaultIncomeAccount: {
5673
6073
  type: 'string',
5674
- description: 'Default income account for the second posting',
6074
+ description:
6075
+ 'Default income account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5675
6076
  example: 'Income:Unknown'
5676
6077
  },
5677
6078
  filterPending: {
@@ -5686,12 +6087,7 @@ export const $ProviderSyncConfigDto = {
5686
6087
  example: 'acc_gocardless_001'
5687
6088
  }
5688
6089
  },
5689
- required: [
5690
- 'sourceAccount',
5691
- 'defaultCurrency',
5692
- 'defaultExpenseAccount',
5693
- 'defaultIncomeAccount'
5694
- ]
6090
+ required: ['sourceAccount', 'defaultCurrency']
5695
6091
  } as const;
5696
6092
 
5697
6093
  export const $ProviderSyncDto = {
@@ -5901,15 +6297,105 @@ export const $UncoveredFormatMissDto = {
5901
6297
  properties: {}
5902
6298
  } as const;
5903
6299
 
6300
+ export const $ClientParsedDataDto = {
6301
+ type: 'object',
6302
+ properties: {
6303
+ amount: {
6304
+ type: 'number',
6305
+ description: 'Transaction amount',
6306
+ example: 35
6307
+ },
6308
+ currency: {
6309
+ type: 'string',
6310
+ description: 'Currency code',
6311
+ example: 'CNY'
6312
+ },
6313
+ date: {
6314
+ type: 'string',
6315
+ description: 'Transaction date (ISO 8601)',
6316
+ example: '2026-08-15'
6317
+ },
6318
+ payee: {
6319
+ type: 'string',
6320
+ description: 'Payee/merchant name',
6321
+ example: 'Starbucks'
6322
+ },
6323
+ narration: {
6324
+ type: 'string',
6325
+ description: 'Transaction narration'
6326
+ },
6327
+ category: {
6328
+ type: 'string',
6329
+ description: 'Category slug',
6330
+ example: 'food_restaurant'
6331
+ },
6332
+ incomeType: {
6333
+ type: 'string',
6334
+ description: 'Income type',
6335
+ example: 'Salary'
6336
+ },
6337
+ incomeSource: {
6338
+ type: 'string',
6339
+ description: 'Income source',
6340
+ example: 'Anthropic Inc.'
6341
+ },
6342
+ symbol: {
6343
+ type: 'string',
6344
+ description: 'Security symbol code (e.g., 600519, AAPL)',
6345
+ example: 'AAPL'
6346
+ },
6347
+ quantity: {
6348
+ type: 'number',
6349
+ description: 'Quantity of shares/units',
6350
+ example: 100
6351
+ },
6352
+ price: {
6353
+ type: 'number',
6354
+ description: 'Unit price per share/unit',
6355
+ example: 1900
6356
+ },
6357
+ investmentAction: {
6358
+ type: 'string',
6359
+ description: 'Investment action',
6360
+ enum: ['buy', 'sell'],
6361
+ example: 'buy'
6362
+ },
6363
+ paymentSource: {
6364
+ type: 'string',
6365
+ description: 'Payment source: asset (default) or liability (credit card)',
6366
+ enum: ['asset', 'liability'],
6367
+ example: 'asset'
6368
+ },
6369
+ liabilityHint: {
6370
+ type: 'string',
6371
+ description: 'Liability account hint (CreditCard/Huabei/Baitiao)',
6372
+ example: 'CreditCard'
6373
+ },
6374
+ warning: {
6375
+ type: 'string',
6376
+ description:
6377
+ 'Display-only warning from the prior response; accepted but ignored.',
6378
+ example: 'Cross-currency settlement applies.'
6379
+ }
6380
+ }
6381
+ } as const;
6382
+
5904
6383
  export const $ProcessNlpDto = {
5905
6384
  type: 'object',
5906
6385
  properties: {
5907
6386
  message: {
5908
6387
  type: 'string',
5909
- description: 'Natural language text describing a transaction (Chinese)',
5910
- example: 'yesterday Starbucks spent 35 yuan',
6388
+ description:
6389
+ 'Natural language text describing a transaction. Optional when `confirm` is true (structured confirm); otherwise required.',
6390
+ example: 'Starbucks 35',
5911
6391
  maxLength: 500
5912
6392
  },
6393
+ confirm: {
6394
+ type: 'boolean',
6395
+ description:
6396
+ 'Structured confirm signal — bypasses NL confirm-word matching when true. Send parsedData field edits alongside. The NL word-list path is the fallback.',
6397
+ example: true
6398
+ },
5913
6399
  sessionId: {
5914
6400
  type: 'string',
5915
6401
  description:
@@ -5917,17 +6403,51 @@ export const $ProcessNlpDto = {
5917
6403
  example: 'session_abc123'
5918
6404
  },
5919
6405
  parsedData: {
5920
- type: 'object',
5921
6406
  description:
5922
6407
  'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
5923
6408
  example: {
5924
6409
  amount: 35,
5925
6410
  currency: 'CNY',
5926
6411
  payee: 'Starbucks'
5927
- }
6412
+ },
6413
+ allOf: [
6414
+ {
6415
+ $ref: '#/components/schemas/ClientParsedDataDto'
6416
+ }
6417
+ ]
6418
+ },
6419
+ selectedRuleId: {
6420
+ type: 'string',
6421
+ description:
6422
+ '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.',
6423
+ example: 'rule_abc123'
6424
+ },
6425
+ selectedAccount: {
6426
+ type: 'string',
6427
+ description:
6428
+ '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.',
6429
+ example: 'Expenses:Food:Coffee'
6430
+ },
6431
+ viewpointAccount: {
6432
+ type: 'string',
6433
+ description:
6434
+ '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.',
6435
+ example: 'Assets:CN:Bank:ICBC'
6436
+ },
6437
+ viewpointCategory: {
6438
+ type: 'string',
6439
+ description:
6440
+ "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.",
6441
+ example: 'Food'
6442
+ },
6443
+ viewpointFlow: {
6444
+ type: 'string',
6445
+ description:
6446
+ "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.",
6447
+ enum: ['income', 'expense'],
6448
+ example: 'expense'
5928
6449
  }
5929
- },
5930
- required: ['message']
6450
+ }
5931
6451
  } as const;
5932
6452
 
5933
6453
  export const $NlpTransactionInfoDto = {
@@ -6239,6 +6759,24 @@ export const $NlpRuleConfirmationDataDto = {
6239
6759
  ]
6240
6760
  } as const;
6241
6761
 
6762
+ export const $NlpAccountCandidateDto = {
6763
+ type: 'object',
6764
+ properties: {
6765
+ path: {
6766
+ type: 'string',
6767
+ description: 'Canonical beancount account path (echo back on selection)',
6768
+ example: 'Expenses:Food:Dining'
6769
+ },
6770
+ name: {
6771
+ type: 'string',
6772
+ description:
6773
+ 'Localized display name (ADR-0114 read-time projection, user locale)',
6774
+ example: '餐饮'
6775
+ }
6776
+ },
6777
+ required: ['path', 'name']
6778
+ } as const;
6779
+
6242
6780
  export const $NlpAccountConfirmationDataDto = {
6243
6781
  type: 'object',
6244
6782
  properties: {
@@ -6249,14 +6787,16 @@ export const $NlpAccountConfirmationDataDto = {
6249
6787
  },
6250
6788
  suggestedAccount: {
6251
6789
  type: 'string',
6252
- description: 'Suggested replacement account',
6790
+ description:
6791
+ 'Suggested replacement account (omitted when no clear candidate)',
6253
6792
  example: 'Expenses:Food:Drinks'
6254
6793
  },
6255
6794
  similarAccounts: {
6256
- description: 'Similar accounts for user selection',
6795
+ description:
6796
+ 'Similar accounts for user selection (path + localized name, #680)',
6257
6797
  type: 'array',
6258
6798
  items: {
6259
- type: 'string'
6799
+ $ref: '#/components/schemas/NlpAccountCandidateDto'
6260
6800
  }
6261
6801
  },
6262
6802
  errorMessage: {
@@ -6271,7 +6811,6 @@ export const $NlpAccountConfirmationDataDto = {
6271
6811
  },
6272
6812
  required: [
6273
6813
  'invalidAccount',
6274
- 'suggestedAccount',
6275
6814
  'similarAccounts',
6276
6815
  'errorMessage',
6277
6816
  'transactionContext'
@@ -6444,7 +6983,8 @@ export const $NlpSuggestedAccountDto = {
6444
6983
  },
6445
6984
  confidence: {
6446
6985
  type: 'number',
6447
- description: 'Confidence score for this suggestion (0-1)',
6986
+ description:
6987
+ 'Confidence score for this suggestion (0-1). Present = predicted (confirm/confirm_rule/confirm_account); omitted = actual persisted account (created). (#586)',
6448
6988
  example: 0.9
6449
6989
  }
6450
6990
  },
@@ -6480,23 +7020,31 @@ export const $NlpDefaultAccountsDto = {
6480
7020
  properties: {
6481
7021
  asset: {
6482
7022
  type: 'string',
6483
- description: 'Default asset account',
6484
- example: 'Assets:Checking'
7023
+ description:
7024
+ 'Default OPEN asset account (MRU when multiple), or null when none/ambiguous',
7025
+ example: 'Assets:Checking',
7026
+ nullable: true
6485
7027
  },
6486
7028
  expense: {
6487
7029
  type: 'string',
6488
- description: 'Default expense account',
6489
- example: 'Expenses:Uncategorized'
7030
+ description:
7031
+ 'Default OPEN expense account (MRU when multiple), or null when none/ambiguous',
7032
+ example: 'Expenses:Food:Coffee',
7033
+ nullable: true
6490
7034
  },
6491
7035
  income: {
6492
7036
  type: 'string',
6493
- description: 'Default income account',
6494
- example: 'Income:Uncategorized'
7037
+ description:
7038
+ 'Default OPEN income account (MRU when multiple), or null when none/ambiguous',
7039
+ example: 'Income:Salary',
7040
+ nullable: true
6495
7041
  },
6496
7042
  liability: {
6497
7043
  type: 'string',
6498
- description: 'Default liability account',
6499
- example: 'Liabilities:CreditCard'
7044
+ description:
7045
+ 'Default OPEN liability account (MRU when multiple), or null when none/ambiguous',
7046
+ example: 'Liabilities:CreditCard',
7047
+ nullable: true
6500
7048
  }
6501
7049
  },
6502
7050
  required: ['asset', 'expense', 'income', 'liability']
@@ -6536,7 +7084,7 @@ export const $NlpResponseDto = {
6536
7084
  type: 'string',
6537
7085
  description:
6538
7086
  'Asset sub-type (only present when intent is "asset"). Determines which asset-related form to render.',
6539
- enum: ['transfer', 'banking', 'investment'],
7087
+ enum: ['transfer', 'banking', 'investment', 'lend', 'lend_collect'],
6540
7088
  example: 'investment'
6541
7089
  },
6542
7090
  liabilitySubType: {
@@ -6684,7 +7232,7 @@ export const $NlpResponseDto = {
6684
7232
  },
6685
7233
  suggestedAccounts: {
6686
7234
  description:
6687
- 'Suggested accounts for this transaction. Contains recommended source and destination accounts based on the detected intent and rules.',
7235
+ '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
7236
  allOf: [
6689
7237
  {
6690
7238
  $ref: '#/components/schemas/NlpSuggestedAccountsDto'
@@ -6693,7 +7241,7 @@ export const $NlpResponseDto = {
6693
7241
  },
6694
7242
  defaultAccounts: {
6695
7243
  description:
6696
- 'Default accounts for the user/region. These are fallback accounts used when no specific suggestion is available.',
7244
+ 'Default fallback accounts for the user/region (#586). v1 returns universal constants; per-user personalization is planned.',
6697
7245
  allOf: [
6698
7246
  {
6699
7247
  $ref: '#/components/schemas/NlpDefaultAccountsDto'
@@ -6739,13 +7287,26 @@ export const $PlatformListItemDto = {
6739
7287
  suggestedSegment: {
6740
7288
  type: 'string',
6741
7289
  description:
6742
- 'Suggested path segment — canonical with first char uppercased (ACC_COMP_NAME_RE)'
7290
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6743
7291
  },
6744
7292
  logoUrl: {
6745
7293
  type: 'string',
6746
7294
  description: 'Logo URL',
6747
7295
  nullable: true
6748
7296
  },
7297
+ countryCode: {
7298
+ type: 'string',
7299
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7300
+ example: 'CN',
7301
+ nullable: true
7302
+ },
7303
+ category: {
7304
+ type: 'string',
7305
+ description:
7306
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7307
+ nullable: true,
7308
+ example: 'DigitalWallet'
7309
+ },
6749
7310
  isBound: {
6750
7311
  type: 'boolean',
6751
7312
  description: 'Whether user has accounts using this platform'
@@ -6759,6 +7320,8 @@ export const $PlatformListItemDto = {
6759
7320
  'canonical',
6760
7321
  'suggestedSegment',
6761
7322
  'logoUrl',
7323
+ 'countryCode',
7324
+ 'category',
6762
7325
  'isBound'
6763
7326
  ]
6764
7327
  } as const;
@@ -6794,13 +7357,26 @@ export const $PlatformMatchResultDto = {
6794
7357
  suggestedSegment: {
6795
7358
  type: 'string',
6796
7359
  description:
6797
- 'Suggested path segment — canonical, already in ACCOUNT_RE format'
7360
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6798
7361
  },
6799
7362
  logoUrl: {
6800
7363
  type: 'string',
6801
7364
  description: 'Logo URL',
6802
7365
  nullable: true
6803
7366
  },
7367
+ countryCode: {
7368
+ type: 'string',
7369
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7370
+ example: 'CN',
7371
+ nullable: true
7372
+ },
7373
+ category: {
7374
+ type: 'string',
7375
+ description:
7376
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7377
+ nullable: true,
7378
+ example: 'DigitalWallet'
7379
+ },
6804
7380
  matchType: {
6805
7381
  type: 'string',
6806
7382
  description: "How this row matched: 'exact' > 'prefix' > 'substring'",
@@ -6814,6 +7390,8 @@ export const $PlatformMatchResultDto = {
6814
7390
  'type',
6815
7391
  'suggestedSegment',
6816
7392
  'logoUrl',
7393
+ 'countryCode',
7394
+ 'category',
6817
7395
  'matchType'
6818
7396
  ]
6819
7397
  } as const;
@@ -6843,7 +7421,80 @@ export const $PlatformMatchResponseDto = {
6843
7421
  description: 'true when total > platforms.length (more matches exist)'
6844
7422
  }
6845
7423
  },
6846
- required: ['platforms', 'matchType', 'total', 'hasMore']
7424
+ required: ['platforms', 'matchType', 'total', 'hasMore']
7425
+ } as const;
7426
+
7427
+ export const $PlatformStandardsPlatformDto = {
7428
+ type: 'object',
7429
+ properties: {
7430
+ id: {
7431
+ type: 'string',
7432
+ description: 'Global platform ID'
7433
+ },
7434
+ name: {
7435
+ type: 'string',
7436
+ description: 'Platform name (e.g., "ICBC")'
7437
+ },
7438
+ canonical: {
7439
+ type: 'string',
7440
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7441
+ },
7442
+ suggestedSegment: {
7443
+ type: 'string',
7444
+ description:
7445
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7446
+ },
7447
+ type: {
7448
+ type: 'string',
7449
+ description: 'Platform type',
7450
+ enum: [
7451
+ 'BANK',
7452
+ 'BROKERAGE',
7453
+ 'CRYPTO_EXCHANGE',
7454
+ 'PAYMENT',
7455
+ 'INVESTMENT',
7456
+ 'INSURANCE',
7457
+ 'OTHER'
7458
+ ]
7459
+ },
7460
+ category: {
7461
+ type: 'string',
7462
+ description:
7463
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank) resolved against the final region. null = no region-aware suggestion; fall back to type.',
7464
+ nullable: true,
7465
+ example: 'Bank'
7466
+ }
7467
+ },
7468
+ required: ['id', 'name', 'canonical', 'suggestedSegment', 'type', 'category']
7469
+ } as const;
7470
+
7471
+ export const $PlatformStandardsResponseDto = {
7472
+ type: 'object',
7473
+ properties: {
7474
+ platform: {
7475
+ description: 'The selected platform (institution lock source)',
7476
+ allOf: [
7477
+ {
7478
+ $ref: '#/components/schemas/PlatformStandardsPlatformDto'
7479
+ }
7480
+ ]
7481
+ },
7482
+ region: {
7483
+ type: 'string',
7484
+ description:
7485
+ "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.",
7486
+ example: 'CN'
7487
+ },
7488
+ templates: {
7489
+ description:
7490
+ 'Candidate account-standard templates of the resolved region (groupable by productCategory client-side)',
7491
+ type: 'array',
7492
+ items: {
7493
+ $ref: '#/components/schemas/AccountStandardResponseDto'
7494
+ }
7495
+ }
7496
+ },
7497
+ required: ['platform', 'region', 'templates']
6847
7498
  } as const;
6848
7499
 
6849
7500
  export const $CreatePlatformDto = {
@@ -6947,7 +7598,8 @@ export const $UpdatePlatformDto = {
6947
7598
  },
6948
7599
  isActive: {
6949
7600
  type: 'boolean',
6950
- description: 'Whether the platform is active'
7601
+ description: 'Whether the platform is active',
7602
+ default: true
6951
7603
  }
6952
7604
  }
6953
7605
  } as const;
@@ -7146,7 +7798,8 @@ export const $PlatformGroupDto = {
7146
7798
  example: 'CMB Bank'
7147
7799
  },
7148
7800
  accounts: {
7149
- description: 'Accounts within this platform',
7801
+ description:
7802
+ 'Accounts within this platform (Assets and Liabilities rows, #696)',
7150
7803
  type: 'array',
7151
7804
  items: {
7152
7805
  $ref: '#/components/schemas/AccountItemDto'
@@ -7154,7 +7807,8 @@ export const $PlatformGroupDto = {
7154
7807
  },
7155
7808
  totalBalance: {
7156
7809
  type: 'string',
7157
- description: 'FX-converted total balance in base currency',
7810
+ description:
7811
+ 'FX-converted total balance in base currency (nets Assets + Liabilities rows; can be negative)',
7158
7812
  example: '100000.00'
7159
7813
  },
7160
7814
  balanceByCurrency: {
@@ -7173,7 +7827,7 @@ export const $PlatformGroupDto = {
7173
7827
  sharePct: {
7174
7828
  type: 'number',
7175
7829
  description:
7176
- 'Share of the grand converted total (0-100); 0 when grand total is 0',
7830
+ '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
7831
  example: 42.5
7178
7832
  }
7179
7833
  },
@@ -7221,7 +7875,8 @@ export const $AccountsSummaryDto = {
7221
7875
  properties: {
7222
7876
  totalAccounts: {
7223
7877
  type: 'number',
7224
- description: 'Total number of accounts'
7878
+ description:
7879
+ 'Total number of accounts (balance sheet: Assets + Liabilities, #696)'
7225
7880
  },
7226
7881
  totalPlatforms: {
7227
7882
  type: 'number',
@@ -7765,7 +8420,7 @@ export const $MonetaryDto = {
7765
8420
  example: 'USD'
7766
8421
  },
7767
8422
  baseCcyEquivalent: {
7768
- type: 'object',
8423
+ type: 'string',
7769
8424
  description: 'Converted to user base currency (Decimal string)',
7770
8425
  example: '21600',
7771
8426
  nullable: true
@@ -7841,13 +8496,13 @@ export const $HoldingPnlRowDto = {
7841
8496
  example: 'Assets:US:Broker:AAPL'
7842
8497
  },
7843
8498
  accountCcy: {
7844
- type: 'object',
8499
+ type: 'string',
7845
8500
  description: 'Account settlement currency (ISO 4217), from cost currency',
7846
8501
  nullable: true,
7847
8502
  example: 'USD'
7848
8503
  },
7849
8504
  brokerType: {
7850
- type: 'object',
8505
+ type: 'string',
7851
8506
  description: 'Broker type derived from Platform.type',
7852
8507
  nullable: true,
7853
8508
  example: 'broker'
@@ -7868,7 +8523,7 @@ export const $HoldingPnlRowDto = {
7868
8523
  example: 'EQUITY'
7869
8524
  },
7870
8525
  assetSubClass: {
7871
- type: 'object',
8526
+ type: 'string',
7872
8527
  nullable: true,
7873
8528
  example: 'STOCK'
7874
8529
  },
@@ -7915,14 +8570,14 @@ export const $HoldingPnlRowDto = {
7915
8570
  ]
7916
8571
  },
7917
8572
  unrealizedPnlBase: {
7918
- type: 'object',
8573
+ type: 'string',
7919
8574
  description:
7920
8575
  'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
7921
8576
  nullable: true,
7922
8577
  example: '6000'
7923
8578
  },
7924
8579
  unrealizedPnlPct: {
7925
- type: 'object',
8580
+ type: 'string',
7926
8581
  description: 'Unrealized P&L % (Decimal string)',
7927
8582
  nullable: true,
7928
8583
  example: '25'
@@ -7946,7 +8601,7 @@ export const $HoldingPnlRowDto = {
7946
8601
  ]
7947
8602
  },
7948
8603
  pctOfInvestedAssets: {
7949
- type: 'object',
8604
+ type: 'string',
7950
8605
  description:
7951
8606
  'Share of invested assets % (Decimal string); only for invested chartTokens',
7952
8607
  nullable: true,
@@ -7991,15 +8646,15 @@ export const $HoldingPnlWarningDto = {
7991
8646
  ]
7992
8647
  },
7993
8648
  symbol: {
7994
- type: 'object',
8649
+ type: 'string',
7995
8650
  nullable: true
7996
8651
  },
7997
8652
  accountId: {
7998
- type: 'object',
8653
+ type: 'string',
7999
8654
  nullable: true
8000
8655
  },
8001
8656
  currency: {
8002
- type: 'object',
8657
+ type: 'string',
8003
8658
  nullable: true
8004
8659
  }
8005
8660
  },
@@ -8062,3 +8717,356 @@ export const $AnonymousLoginResponseDto = {
8062
8717
  },
8063
8718
  required: ['authToken']
8064
8719
  } as const;
8720
+
8721
+ export const $ParserContributionMetaDto = {
8722
+ type: 'object',
8723
+ properties: {
8724
+ institution: {
8725
+ type: 'string',
8726
+ description: 'Institution slug (lowercase kebab-case)',
8727
+ pattern: '^[a-z0-9]+(-[a-z0-9]+)*$',
8728
+ example: 'icbc'
8729
+ },
8730
+ region: {
8731
+ type: 'string',
8732
+ enum: [
8733
+ 'cn',
8734
+ 'us',
8735
+ 'de',
8736
+ 'fr',
8737
+ 'gb',
8738
+ 'hk',
8739
+ 'jp',
8740
+ 'sg',
8741
+ 'au',
8742
+ 'ca',
8743
+ 'other'
8744
+ ]
8745
+ },
8746
+ accountType: {
8747
+ type: 'string',
8748
+ enum: ['checking', 'savings', 'credit', 'debit', 'investment']
8749
+ },
8750
+ format: {
8751
+ type: 'string',
8752
+ enum: ['csv', 'xlsx', 'pdf', 'ofx', 'qif']
8753
+ },
8754
+ institutionDisplayName: {
8755
+ type: 'string',
8756
+ example: '中国工商银行'
8757
+ },
8758
+ encoding: {
8759
+ type: 'string',
8760
+ example: 'utf-8'
8761
+ },
8762
+ delimiter: {
8763
+ type: 'string',
8764
+ description: 'CSV delimiter character: ",", ";", "\\t" or "|"'
8765
+ },
8766
+ headerRows: {
8767
+ type: 'number',
8768
+ default: 1,
8769
+ description: 'Header row count; the client omits the field when it is 1'
8770
+ },
8771
+ notes: {
8772
+ type: 'string',
8773
+ maxLength: 2000
8774
+ }
8775
+ },
8776
+ required: ['institution', 'region', 'accountType', 'format']
8777
+ } as const;
8778
+
8779
+ export const $ParserContributionSamplesDto = {
8780
+ type: 'object',
8781
+ properties: {
8782
+ rows: {
8783
+ description:
8784
+ 'Client-sanitized sample rows (key = column name, value = cell)',
8785
+ type: 'array',
8786
+ items: {
8787
+ type: 'object'
8788
+ }
8789
+ },
8790
+ rawHeaders: {
8791
+ type: 'array',
8792
+ items: {
8793
+ type: 'string'
8794
+ }
8795
+ }
8796
+ },
8797
+ required: ['rows']
8798
+ } as const;
8799
+
8800
+ export const $FieldHintDto = {
8801
+ type: 'object',
8802
+ properties: {
8803
+ columnName: {
8804
+ type: 'string',
8805
+ example: '交易日期'
8806
+ },
8807
+ format: {
8808
+ type: 'string',
8809
+ description: 'Date format, e.g. yyyy-MM-dd HH:mm',
8810
+ example: 'yyyy-MM-dd'
8811
+ },
8812
+ signConvention: {
8813
+ type: 'string',
8814
+ enum: ['negative-expense', 'positive-expense', 'separate-columns']
8815
+ },
8816
+ creditColumn: {
8817
+ type: 'string'
8818
+ },
8819
+ debitColumn: {
8820
+ type: 'string'
8821
+ }
8822
+ },
8823
+ required: ['columnName']
8824
+ } as const;
8825
+
8826
+ export const $ParserContributionFieldHintsDto = {
8827
+ type: 'object',
8828
+ properties: {
8829
+ date: {
8830
+ $ref: '#/components/schemas/FieldHintDto'
8831
+ },
8832
+ amount: {
8833
+ $ref: '#/components/schemas/FieldHintDto'
8834
+ },
8835
+ description: {
8836
+ $ref: '#/components/schemas/FieldHintDto'
8837
+ },
8838
+ balance: {
8839
+ $ref: '#/components/schemas/FieldHintDto'
8840
+ },
8841
+ payee: {
8842
+ $ref: '#/components/schemas/FieldHintDto'
8843
+ },
8844
+ reference: {
8845
+ $ref: '#/components/schemas/FieldHintDto'
8846
+ },
8847
+ category: {
8848
+ $ref: '#/components/schemas/FieldHintDto'
8849
+ }
8850
+ },
8851
+ required: ['date', 'amount']
8852
+ } as const;
8853
+
8854
+ export const $ExpectedTransactionDto = {
8855
+ type: 'object',
8856
+ properties: {
8857
+ date: {
8858
+ type: 'string',
8859
+ example: '2026-08-01'
8860
+ },
8861
+ amount: {
8862
+ type: 'number',
8863
+ example: -45.5
8864
+ },
8865
+ description: {
8866
+ type: 'string',
8867
+ example: '星巴克-***店'
8868
+ },
8869
+ payee: {
8870
+ type: 'string'
8871
+ },
8872
+ category: {
8873
+ type: 'string'
8874
+ }
8875
+ },
8876
+ required: ['date', 'amount', 'description']
8877
+ } as const;
8878
+
8879
+ export const $ParserContributionExamplesDto = {
8880
+ type: 'object',
8881
+ properties: {
8882
+ expectedTransactions: {
8883
+ type: 'array',
8884
+ items: {
8885
+ $ref: '#/components/schemas/ExpectedTransactionDto'
8886
+ }
8887
+ }
8888
+ },
8889
+ required: ['expectedTransactions']
8890
+ } as const;
8891
+
8892
+ export const $ParserContributionRequestDto = {
8893
+ type: 'object',
8894
+ properties: {
8895
+ meta: {
8896
+ $ref: '#/components/schemas/ParserContributionMetaDto'
8897
+ },
8898
+ samples: {
8899
+ $ref: '#/components/schemas/ParserContributionSamplesDto'
8900
+ },
8901
+ fieldHints: {
8902
+ $ref: '#/components/schemas/ParserContributionFieldHintsDto'
8903
+ },
8904
+ examples: {
8905
+ description: 'Omitted entirely by the client when empty',
8906
+ allOf: [
8907
+ {
8908
+ $ref: '#/components/schemas/ParserContributionExamplesDto'
8909
+ }
8910
+ ]
8911
+ }
8912
+ },
8913
+ required: ['meta', 'samples', 'fieldHints']
8914
+ } as const;
8915
+
8916
+ export const $ParserContributionRelayResponseDto = {
8917
+ type: 'object',
8918
+ properties: {
8919
+ issueUrl: {
8920
+ type: 'string',
8921
+ example: 'https://github.com/fire-zu/firela-vlt/issues/42'
8922
+ },
8923
+ issueNumber: {
8924
+ type: 'number',
8925
+ example: 42
8926
+ }
8927
+ },
8928
+ required: ['issueUrl', 'issueNumber']
8929
+ } as const;
8930
+
8931
+ export const $SymbolSearchResultDto = {
8932
+ type: 'object',
8933
+ properties: {
8934
+ symbol: {
8935
+ type: 'string',
8936
+ example: 'AAPL'
8937
+ },
8938
+ name: {
8939
+ type: 'string',
8940
+ example: 'Apple Inc.',
8941
+ nullable: true
8942
+ },
8943
+ exchange: {
8944
+ type: 'string',
8945
+ example: 'US',
8946
+ nullable: true
8947
+ },
8948
+ assetType: {
8949
+ type: 'string',
8950
+ description: 'OpenBB asset_type (e.g. stock, etf)',
8951
+ example: 'stock',
8952
+ nullable: true
8953
+ },
8954
+ assetClass: {
8955
+ type: 'string',
8956
+ description: 'IGN asset class (region.types.ts ASSET_CLASSES)',
8957
+ example: 'EQUITY',
8958
+ nullable: true
8959
+ },
8960
+ assetSubClass: {
8961
+ type: 'string',
8962
+ description: 'IGN asset sub-class (region.types.ts ASSET_SUB_CLASSES)',
8963
+ example: 'STOCK',
8964
+ nullable: true
8965
+ },
8966
+ currency: {
8967
+ type: 'string',
8968
+ description: 'Trading currency (extra_data or inferred from exchange)',
8969
+ example: 'USD',
8970
+ nullable: true
8971
+ }
8972
+ },
8973
+ required: ['symbol']
8974
+ } as const;
8975
+
8976
+ export const $SymbolQuoteDto = {
8977
+ type: 'object',
8978
+ properties: {
8979
+ symbol: {
8980
+ type: 'string',
8981
+ example: 'AAPL'
8982
+ },
8983
+ name: {
8984
+ type: 'string',
8985
+ example: 'Apple Inc.',
8986
+ nullable: true
8987
+ },
8988
+ exchange: {
8989
+ type: 'string',
8990
+ example: 'US',
8991
+ nullable: true
8992
+ },
8993
+ assetType: {
8994
+ type: 'string',
8995
+ description: 'OpenBB asset_type',
8996
+ example: 'stock',
8997
+ nullable: true
8998
+ },
8999
+ assetClass: {
9000
+ type: 'string',
9001
+ description: 'IGN asset class',
9002
+ example: 'EQUITY',
9003
+ nullable: true
9004
+ },
9005
+ assetSubClass: {
9006
+ type: 'string',
9007
+ description: 'IGN asset sub-class',
9008
+ example: 'STOCK',
9009
+ nullable: true
9010
+ },
9011
+ currency: {
9012
+ type: 'string',
9013
+ description: 'Trading currency (extra_data or inferred from exchange)',
9014
+ example: 'USD',
9015
+ nullable: true
9016
+ },
9017
+ price: {
9018
+ type: 'string',
9019
+ description: 'Latest price (Decimal string)',
9020
+ example: '189.84',
9021
+ nullable: true
9022
+ },
9023
+ priceDate: {
9024
+ type: 'string',
9025
+ description: 'Date the price was observed (ISO yyyy-MM-dd)',
9026
+ example: '2026-08-05',
9027
+ nullable: true
9028
+ },
9029
+ changePercent: {
9030
+ type: 'number',
9031
+ description:
9032
+ '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.',
9033
+ example: 1.7,
9034
+ nullable: true
9035
+ },
9036
+ prevClose: {
9037
+ type: 'string',
9038
+ description: 'Previous close (Decimal string)',
9039
+ nullable: true
9040
+ },
9041
+ open: {
9042
+ type: 'string',
9043
+ description: 'Day open (Decimal string)',
9044
+ nullable: true
9045
+ },
9046
+ high: {
9047
+ type: 'string',
9048
+ description: 'Day high (Decimal string)',
9049
+ nullable: true
9050
+ },
9051
+ low: {
9052
+ type: 'string',
9053
+ description: 'Day low (Decimal string)',
9054
+ nullable: true
9055
+ },
9056
+ volume: {
9057
+ type: 'string',
9058
+ description: 'Day volume (Decimal string)',
9059
+ nullable: true
9060
+ },
9061
+ yearHigh: {
9062
+ type: 'string',
9063
+ description: '52-week high (Decimal string)',
9064
+ nullable: true
9065
+ },
9066
+ yearLow: {
9067
+ type: 'string',
9068
+ description: '52-week low (Decimal string)',
9069
+ nullable: true
9070
+ }
9071
+ }
9072
+ } as const;