@firela/api-types 0.0.0-canary.97006feb → 0.0.0-canary.97643e2c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,6 +51,14 @@ export const $CreateAccountDto = {
51
51
  description: 'Icon identifier (overrides template)',
52
52
  example: 'bank-custom'
53
53
  },
54
+ displayName: {
55
+ type: 'string',
56
+ description:
57
+ 'User-set display name override (omit/null = keep the derived name)',
58
+ nullable: true,
59
+ maxLength: 50,
60
+ example: 'Salary card'
61
+ },
54
62
  openDirectiveMeta: {
55
63
  type: 'object',
56
64
  description:
@@ -88,6 +96,49 @@ export const $AccountResponseDto = {
88
96
  enum: ['Assets', 'Liabilities', 'Income', 'Expenses', 'Equity'],
89
97
  example: 'Assets'
90
98
  },
99
+ assetSubClass: {
100
+ type: 'string',
101
+ description:
102
+ 'Account-level asset sub-class (product type, e.g. STOCK/DEPOSIT/CREDIT_CARD/PERSONAL_LOAN). Computed from the account path via the asset-classifier (ADR-0077). Null for non-asset accounts (Income/Expenses/Equity) or unmatched paths.',
103
+ enum: [
104
+ 'DEPOSIT',
105
+ 'CASH',
106
+ 'MONEY_MARKET_FUND',
107
+ 'STOCK',
108
+ 'ETF',
109
+ 'MUTUAL_FUND',
110
+ 'EQUITY_COMPENSATION',
111
+ 'GOVERNMENT_BOND',
112
+ 'CORPORATE_BOND',
113
+ 'BOND_FUND',
114
+ 'PRIMARY_RESIDENCE',
115
+ 'INVESTMENT_PROPERTY',
116
+ 'REIT',
117
+ 'GOLD',
118
+ 'SILVER',
119
+ 'PRECIOUS_METAL',
120
+ 'PRECIOUS_METAL_FUND',
121
+ 'COMMODITY',
122
+ 'COMMODITY_FUND',
123
+ 'CRYPTOCURRENCY',
124
+ 'RETIREMENT_ACCOUNT',
125
+ 'HEALTH_ACCOUNT',
126
+ 'EDUCATION_ACCOUNT',
127
+ 'INSURANCE',
128
+ 'PRIVATE_EQUITY',
129
+ 'HEDGE_FUND',
130
+ 'COLLECTIBLES',
131
+ 'MORTGAGE',
132
+ 'STUDENT_LOAN',
133
+ 'CREDIT_CARD',
134
+ 'PERSONAL_LOAN',
135
+ 'ACCOUNTS_PAYABLE',
136
+ 'TAX_PAYABLE',
137
+ 'OTHER'
138
+ ],
139
+ nullable: true,
140
+ example: 'STOCK'
141
+ },
91
142
  status: {
92
143
  type: 'string',
93
144
  description: 'Account status',
@@ -138,7 +189,8 @@ export const $AccountResponseDto = {
138
189
  },
139
190
  displayName: {
140
191
  type: 'string',
141
- description: 'Localized display name (ADR-0114, read-time projection)',
192
+ description:
193
+ 'Display name with precedence: user-set name (#762) > ADR-0114 localized name > path leaf (read-time projection)',
142
194
  example: 'Checking'
143
195
  },
144
196
  icon: {
@@ -154,7 +206,7 @@ export const $AccountResponseDto = {
154
206
  }
155
207
  },
156
208
  platformId: {
157
- type: 'object',
209
+ type: 'string',
158
210
  description: 'Platform ID (null if unbound)',
159
211
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
160
212
  },
@@ -234,6 +286,14 @@ export const $UpdateAccountDto = {
234
286
  description: 'Icon identifier',
235
287
  example: 'bank-custom'
236
288
  },
289
+ displayName: {
290
+ type: 'string',
291
+ description:
292
+ 'User-set display name override (null = clear the override and fall back to the derived name, omit = unchanged)',
293
+ nullable: true,
294
+ maxLength: 50,
295
+ example: 'Salary card'
296
+ },
237
297
  openDirectiveMeta: {
238
298
  type: 'object',
239
299
  description:
@@ -334,16 +394,43 @@ export const $AccountStandardResponseDto = {
334
394
  },
335
395
  name: {
336
396
  type: 'string',
337
- description: 'Short localized display name',
397
+ description:
398
+ 'Short display name. Universal rows project to the request locale (Accept-Language); regional rows keep the authored native name — mixed-language by design (ADR-0131 class P vs class A).',
338
399
  example: 'Housing Fund'
339
400
  },
401
+ aliases: {
402
+ description:
403
+ 'Authored market-language alternative names delivered verbatim (not localized copy, not xlf-managed, not locale-projected). Flat string[] per ADR-0129 D1; ADR-0131 class A.',
404
+ example: ['Alipay', 'WeChat Pay'],
405
+ type: 'array',
406
+ items: {
407
+ type: 'string'
408
+ }
409
+ },
410
+ searchTerms: {
411
+ description:
412
+ 'Locale-projected search synonyms (e.g. the zh bank-card / debit-card everyday terms for the checking account). Pure-locale projection — absent when the locale has no seeded synonyms; English fallback rides the authored aliases field. Search-only vocabulary, not the NLP routing corpus (#698, ADR-0131 fourth-class adjudication).',
413
+ example: ['yinhangka', 'jiejika'],
414
+ type: 'array',
415
+ items: {
416
+ type: 'string'
417
+ }
418
+ },
419
+ currency: {
420
+ type: 'string',
421
+ description:
422
+ 'Product denomination as a 3-letter ISO 4217 code, authored market data delivered verbatim (not localized, not xlf-managed). ADR-0131 class A. Absent = single-currency not asserted — consumers fall back to their own region currency (#714).',
423
+ example: 'HKD'
424
+ },
340
425
  description: {
341
426
  type: 'string',
342
- description: 'Account description (stable semantics only)',
427
+ description:
428
+ 'Account description (stable semantics only). Mixed-language contract: universal rows project to the request locale via the accountDesc xlf axis with an en fallback (ADR-0131 class P, ADR-0132; unseeded locales falling back to English are expected); regional rows deliver the authored market language (ADR-0131 class A, verbatim, never xlf-managed).',
343
429
  example: 'ICBC checking account for daily transactions'
344
430
  },
345
431
  tags: {
346
- description: 'Account tags for categorization',
432
+ description:
433
+ 'Account tags for categorization — structured metadata delivered verbatim (not localized, not xlf-managed). ADR-0131 class A.',
347
434
  example: ['bank', 'checking', 'primary'],
348
435
  type: 'array',
349
436
  items: {
@@ -354,9 +441,47 @@ export const $AccountStandardResponseDto = {
354
441
  type: 'string',
355
442
  description: 'Icon identifier for UI display',
356
443
  example: 'bank-icbc'
444
+ },
445
+ productCategory: {
446
+ type: 'string',
447
+ description:
448
+ 'Onboarding product category (coarse grouping derived from assetSubClass)',
449
+ enum: [
450
+ 'cash',
451
+ 'investment',
452
+ 'credit_card',
453
+ 'loan',
454
+ 'payable_tax',
455
+ 'other'
456
+ ],
457
+ example: 'investment'
458
+ },
459
+ assetClass: {
460
+ type: 'string',
461
+ description:
462
+ 'Asset class (LIQUIDITY/EQUITY/.../LIABILITY), derived at read time from classification rules',
463
+ enum: [
464
+ 'LIQUIDITY',
465
+ 'EQUITY',
466
+ 'FIXED_INCOME',
467
+ 'PRECIOUS_METALS',
468
+ 'COMMODITY',
469
+ 'INSURANCE',
470
+ 'ALTERNATIVE_INVESTMENT',
471
+ 'PERSONAL_ASSETS',
472
+ 'LIABILITY',
473
+ 'REAL_ESTATE',
474
+ 'INDEX'
475
+ ]
476
+ },
477
+ assetSubClass: {
478
+ type: 'string',
479
+ description:
480
+ 'Asset sub-class (product type, derived at read time from classification rules)',
481
+ example: 'STOCK'
357
482
  }
358
483
  },
359
- required: ['path', 'type', 'description', 'tags', 'icon']
484
+ required: ['path', 'type', 'description', 'tags', 'icon', 'productCategory']
360
485
  } as const;
361
486
 
362
487
  export const $AccountStandardListResponseDto = {
@@ -417,7 +542,10 @@ export const $RegionConfigDto = {
417
542
  },
418
543
  locale: {
419
544
  type: 'string',
420
- example: 'de-DE'
545
+ example: 'de-DE',
546
+ pattern: '^[a-z]{2,8}-[A-Z]{2}$',
547
+ description:
548
+ "Region-qualified BCP-47 tag whose region subtag equals the region's own ISO 3166-1 code (e.g., ja-JP, zh-CN, zh-HK)"
421
549
  }
422
550
  },
423
551
  required: ['currency', 'dateFormat', 'locale']
@@ -430,6 +558,12 @@ export const $RegionInfoDto = {
430
558
  type: 'string',
431
559
  example: 'de'
432
560
  },
561
+ open: {
562
+ type: 'boolean',
563
+ example: true,
564
+ description:
565
+ 'Whether the region is open (has a ready regional account template). Not-yet-open regions still return identity metadata and degrade to the universal-only catalog.'
566
+ },
433
567
  displayName: {
434
568
  type: 'string',
435
569
  example: 'Germany'
@@ -448,7 +582,7 @@ export const $RegionInfoDto = {
448
582
  $ref: '#/components/schemas/RegionConfigDto'
449
583
  }
450
584
  },
451
- required: ['code', 'displayName', 'chain', 'config']
585
+ required: ['code', 'open', 'displayName', 'chain', 'config']
452
586
  } as const;
453
587
 
454
588
  export const $RegionsMetadataResponseDto = {
@@ -695,7 +829,7 @@ export const $PostingResponseDto = {
695
829
  units: {
696
830
  type: 'string',
697
831
  description:
698
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
832
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
699
833
  example: '100.50'
700
834
  },
701
835
  currency: {
@@ -1061,7 +1195,7 @@ export const $PostingDetailDto = {
1061
1195
  units: {
1062
1196
  type: 'string',
1063
1197
  description:
1064
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
1198
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
1065
1199
  example: '100.50'
1066
1200
  },
1067
1201
  currency: {
@@ -1245,6 +1379,147 @@ export const $TransactionDetailDto = {
1245
1379
  ]
1246
1380
  } as const;
1247
1381
 
1382
+ export const $TransactionListItemDto = {
1383
+ type: 'object',
1384
+ properties: {
1385
+ id: {
1386
+ type: 'string',
1387
+ description: 'Transaction ID',
1388
+ example: 'clh1234567890abcdef'
1389
+ },
1390
+ date: {
1391
+ type: 'string',
1392
+ description: 'Transaction date',
1393
+ example: '2024-11-28'
1394
+ },
1395
+ flag: {
1396
+ type: 'string',
1397
+ description: 'Transaction flag',
1398
+ enum: [
1399
+ 'CLEARED',
1400
+ 'PENDING',
1401
+ 'PADDING',
1402
+ 'SUMMARIZE',
1403
+ 'TRANSFER',
1404
+ 'CONVERSIONS'
1405
+ ],
1406
+ example: 'CLEARED'
1407
+ },
1408
+ customFlag: {
1409
+ type: 'string',
1410
+ description: 'Custom flag (if not using standard flags)',
1411
+ example: 'R'
1412
+ },
1413
+ payee: {
1414
+ type: 'string',
1415
+ description: 'Payee name',
1416
+ example: 'Whole Foods Market'
1417
+ },
1418
+ narration: {
1419
+ type: 'string',
1420
+ description: 'Transaction narration',
1421
+ example: 'Grocery shopping'
1422
+ },
1423
+ tags: {
1424
+ description: 'Transaction tags',
1425
+ example: ['groceries'],
1426
+ type: 'array',
1427
+ items: {
1428
+ type: 'string'
1429
+ }
1430
+ },
1431
+ links: {
1432
+ description: 'Transaction links',
1433
+ example: ['invoice-2024-001'],
1434
+ type: 'array',
1435
+ items: {
1436
+ type: 'string'
1437
+ }
1438
+ },
1439
+ meta: {
1440
+ type: 'object',
1441
+ description: 'Transaction metadata'
1442
+ },
1443
+ status: {
1444
+ type: 'string',
1445
+ description: 'Transaction status',
1446
+ enum: ['ACTIVE', 'VOIDED', 'SUPERSEDED'],
1447
+ example: 'ACTIVE'
1448
+ },
1449
+ sourceType: {
1450
+ type: 'string',
1451
+ description:
1452
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1453
+ },
1454
+ sourcePlatform: {
1455
+ type: 'string',
1456
+ description: 'Source platform (e.g., alipay, wechat)',
1457
+ example: 'alipay'
1458
+ },
1459
+ postings: {
1460
+ description: 'Transaction postings',
1461
+ type: 'array',
1462
+ items: {
1463
+ $ref: '#/components/schemas/PostingDetailDto'
1464
+ }
1465
+ },
1466
+ createdAt: {
1467
+ type: 'string',
1468
+ description: 'Created at timestamp',
1469
+ example: '2024-11-28T10:30:00.000Z'
1470
+ },
1471
+ voidedAt: {
1472
+ type: 'string',
1473
+ description: 'Voided at timestamp (if voided)',
1474
+ example: '2024-11-29T15:00:00.000Z'
1475
+ },
1476
+ voidedBy: {
1477
+ type: 'string',
1478
+ description: 'User ID who voided this transaction',
1479
+ example: 'clh1234567890abcdef'
1480
+ },
1481
+ correctionReason: {
1482
+ type: 'string',
1483
+ description: 'Correction reason (if voided or superseded)',
1484
+ example: 'Duplicate entry'
1485
+ },
1486
+ supersededBy: {
1487
+ type: 'string',
1488
+ description:
1489
+ 'ID of the transaction that supersedes this one (set when status=SUPERSEDED)',
1490
+ example: 'clh1234567890abcdef'
1491
+ },
1492
+ originalTxn: {
1493
+ type: 'string',
1494
+ description:
1495
+ 'ID of the transaction this one corrected/replaced (back-link on the replacement)',
1496
+ example: 'clh1234567890abcdef'
1497
+ },
1498
+ viewpointAmount: {
1499
+ type: 'string',
1500
+ description:
1501
+ 'Row amount under the request viewpoint (ADR-0126). Category viewpoint (category + flow): per-leg sign-normalized sum over the category account set (Income-root legs negated, Expenses-root identity) — positive under normal booking but NOT clamped (explicit negative expense legs and net-flip refund months stay negative). No viewpoint (plain list / search, no accountId): wallet money-flow net = raw-sign sum over cost-less Assets/Liabilities legs (income positive, expenses negative, transfers net ~0); color cue is the wallet sign (net < 0 = wealth-decreasing). Status-orthogonal: audit views match too (ADR-0128 amount-as-matching-key). Omitted under the account viewpoint (incl. dual) and for rows with no wallet leg.',
1502
+ example: '10000.00'
1503
+ },
1504
+ viewpointCurrency: {
1505
+ type: 'string',
1506
+ description:
1507
+ 'Currency of viewpointAmount. A row spanning multiple currencies takes the largest-magnitude currency group (known simplification, ADR-0126).',
1508
+ example: 'CNY'
1509
+ }
1510
+ },
1511
+ required: [
1512
+ 'id',
1513
+ 'date',
1514
+ 'narration',
1515
+ 'tags',
1516
+ 'links',
1517
+ 'status',
1518
+ 'postings',
1519
+ 'createdAt'
1520
+ ]
1521
+ } as const;
1522
+
1248
1523
  export const $BalanceByCurrencyDto = {
1249
1524
  type: 'object',
1250
1525
  properties: {
@@ -1290,7 +1565,7 @@ export const $TransactionListSummaryDto = {
1290
1565
  totalAmount: {
1291
1566
  type: 'string',
1292
1567
  description:
1293
- 'Partial converted total in base currency (rated currencies only, raw Beancount sign). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1568
+ 'Partial converted total in base currency (rated currencies only). Sign by viewpoint (ADR-0126): account viewpoint keeps the raw Beancount sign (income negative); category viewpoint is per-leg sign-normalized (Income legs negated, Expenses legs identity — positive under normal booking, not clamped). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1294
1569
  example: '-6000.00'
1295
1570
  },
1296
1571
  currency: {
@@ -1316,6 +1591,26 @@ export const $TransactionListSummaryDto = {
1316
1591
  required: ['totalAmount', 'currency', 'balanceByCurrency']
1317
1592
  } as const;
1318
1593
 
1594
+ export const $TransactionListViewpointDto = {
1595
+ type: 'object',
1596
+ properties: {
1597
+ type: {
1598
+ type: 'string',
1599
+ description:
1600
+ 'Viewpoint type (only category drill-down carries a viewpoint today)',
1601
+ enum: ['category'],
1602
+ example: 'category'
1603
+ },
1604
+ flow: {
1605
+ type: 'string',
1606
+ description: 'Flow root the category account set is restricted to',
1607
+ enum: ['income', 'expense'],
1608
+ example: 'expense'
1609
+ }
1610
+ },
1611
+ required: ['type', 'flow']
1612
+ } as const;
1613
+
1319
1614
  export const $TransactionListResponseDto = {
1320
1615
  type: 'object',
1321
1616
  properties: {
@@ -1323,7 +1618,7 @@ export const $TransactionListResponseDto = {
1323
1618
  description: 'List of transactions',
1324
1619
  type: 'array',
1325
1620
  items: {
1326
- $ref: '#/components/schemas/TransactionDetailDto'
1621
+ $ref: '#/components/schemas/TransactionListItemDto'
1327
1622
  }
1328
1623
  },
1329
1624
  total: {
@@ -1349,6 +1644,15 @@ export const $TransactionListResponseDto = {
1349
1644
  $ref: '#/components/schemas/TransactionListSummaryDto'
1350
1645
  }
1351
1646
  ]
1647
+ },
1648
+ viewpoint: {
1649
+ description:
1650
+ 'Viewpoint metadata (ADR-0126). Present only for a single category filter (category + flow, no accountId); dual-perspective requests are viewpoint-less (raw signs, no viewpointAmount).',
1651
+ allOf: [
1652
+ {
1653
+ $ref: '#/components/schemas/TransactionListViewpointDto'
1654
+ }
1655
+ ]
1352
1656
  }
1353
1657
  },
1354
1658
  required: ['data', 'total', 'limit', 'offset']
@@ -2259,10 +2563,10 @@ export const $UpdatePayeeDto = {
2259
2563
  meta: {
2260
2564
  type: 'object',
2261
2565
  description:
2262
- 'Metadata for extended information (location, notes, contact info, etc.). Will merge with existing metadata.',
2566
+ 'Metadata for extended information (location, notes, contact info, etc.)',
2263
2567
  example: {
2264
2568
  location: 'Zhongguancun',
2265
- note: 'Updated note',
2569
+ note: 'Near subway station',
2266
2570
  favorite: true
2267
2571
  }
2268
2572
  },
@@ -2823,568 +3127,660 @@ export const $UpdateCommodityDto = {
2823
3127
  }
2824
3128
  } as const;
2825
3129
 
2826
- export const $CreateBeanPriceDto = {
3130
+ export const $CurrencyBalanceDto = {
2827
3131
  type: 'object',
2828
3132
  properties: {
2829
3133
  currency: {
2830
3134
  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)',
3135
+ description: 'ISO 4217 currency code',
2837
3136
  example: 'CNY'
2838
3137
  },
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: {
3138
+ balance: {
2847
3139
  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
- }
3140
+ description: 'Balance amount',
3141
+ example: '500000.00'
2860
3142
  }
2861
3143
  },
2862
- required: ['currency', 'quoteCurrency', 'amount', 'date']
3144
+ required: ['currency', 'balance']
2863
3145
  } as const;
2864
3146
 
2865
- export const $PriceResponseDto = {
3147
+ export const $TimeSeriesPointDto = {
2866
3148
  type: 'object',
2867
3149
  properties: {
2868
- id: {
3150
+ date: {
2869
3151
  type: 'string',
2870
- description: 'Unique identifier',
2871
- example: 'uuid-123-456'
3152
+ description: 'Date in YYYY-MM-DD format',
3153
+ example: '2024-06-15'
2872
3154
  },
2873
- userId: {
3155
+ value: {
2874
3156
  type: 'string',
2875
- description: 'User ID (owner of the price)',
2876
- example: 'user-123'
3157
+ description: 'Value at this date (in base currency)',
3158
+ example: '500000.00'
2877
3159
  },
2878
- currency: {
3160
+ change: {
2879
3161
  type: 'string',
2880
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2881
- example: 'BTC'
3162
+ description: 'Change from previous point',
3163
+ example: '5000.00'
2882
3164
  },
2883
- quoteCurrency: {
3165
+ assets: {
2884
3166
  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
3167
+ description: 'Total assets at this date (in base currency)',
3168
+ example: '494338.00'
2893
3169
  },
2894
- date: {
3170
+ liabilities: {
2895
3171
  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'
3172
+ description: 'Total liabilities at this date (in base currency)',
3173
+ example: '310098.00'
2900
3174
  },
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
3175
+ byCurrency: {
3176
+ description: 'Multi-currency breakdown for this point',
3177
+ type: 'array',
3178
+ items: {
3179
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2909
3180
  }
3181
+ }
3182
+ },
3183
+ required: ['date', 'value']
3184
+ } as const;
3185
+
3186
+ export const $TrendSummaryDto = {
3187
+ type: 'object',
3188
+ properties: {
3189
+ startValue: {
3190
+ type: 'string',
3191
+ description: 'Value at start of period',
3192
+ example: '450000.00'
2910
3193
  },
2911
- createdAt: {
2912
- format: 'date-time',
3194
+ endValue: {
2913
3195
  type: 'string',
2914
- description: 'Creation timestamp',
2915
- example: '2024-11-03T10:00:00Z'
3196
+ description: 'Value at end of period',
3197
+ example: '500000.00'
2916
3198
  },
2917
- updatedAt: {
2918
- format: 'date-time',
3199
+ totalChange: {
2919
3200
  type: 'string',
2920
- description: 'Last update timestamp',
2921
- example: '2024-11-03T10:00:00Z'
3201
+ description: 'Total change over period',
3202
+ example: '50000.00'
3203
+ },
3204
+ totalChangePercentage: {
3205
+ type: 'string',
3206
+ description: 'Total change percentage',
3207
+ example: '+11.11%'
2922
3208
  }
2923
3209
  },
2924
- required: [
2925
- 'id',
2926
- 'userId',
2927
- 'currency',
2928
- 'quoteCurrency',
2929
- 'amount',
2930
- 'date',
2931
- 'meta',
2932
- 'createdAt',
2933
- 'updatedAt'
2934
- ]
3210
+ required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
2935
3211
  } as const;
2936
3212
 
2937
- export const $PriceListResponseDto = {
3213
+ export const $MultiCurrencyPointDto = {
2938
3214
  type: 'object',
2939
3215
  properties: {
2940
- items: {
2941
- description: 'List of prices',
3216
+ date: {
3217
+ type: 'string',
3218
+ description: 'Date in YYYY-MM-DD format',
3219
+ example: '2024-06-15'
3220
+ },
3221
+ byCurrency: {
3222
+ description: 'Balances by currency',
2942
3223
  type: 'array',
2943
3224
  items: {
2944
- $ref: '#/components/schemas/PriceResponseDto'
3225
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2945
3226
  }
2946
- },
2947
- total: {
2948
- type: 'number',
2949
- description: 'Total number of prices',
2950
- example: 42
2951
3227
  }
2952
3228
  },
2953
- required: ['items', 'total']
3229
+ required: ['date', 'byCurrency']
2954
3230
  } as const;
2955
3231
 
2956
- export const $UpdateBeanPriceDto = {
3232
+ export const $PortfolioTrendsResponseDto = {
2957
3233
  type: 'object',
2958
3234
  properties: {
2959
- currency: {
2960
- type: 'string',
2961
- description: 'Currency being priced'
3235
+ series: {
3236
+ description: 'Time series data points',
3237
+ type: 'array',
3238
+ items: {
3239
+ $ref: '#/components/schemas/TimeSeriesPointDto'
3240
+ }
2962
3241
  },
2963
- quoteCurrency: {
3242
+ summary: {
3243
+ description: 'Period summary',
3244
+ allOf: [
3245
+ {
3246
+ $ref: '#/components/schemas/TrendSummaryDto'
3247
+ }
3248
+ ]
3249
+ },
3250
+ period: {
2964
3251
  type: 'string',
2965
- description: 'Quote currency (pricing currency)'
3252
+ description: 'Period requested',
3253
+ example: '6m'
2966
3254
  },
2967
- amount: {
2968
- type: 'number',
2969
- description: 'Price amount (MUST be >= 0 per Beancount spec)',
2970
- minimum: 0
3255
+ granularity: {
3256
+ type: 'string',
3257
+ description: 'Data granularity',
3258
+ example: 'month'
2971
3259
  },
2972
- date: {
3260
+ currency: {
2973
3261
  type: 'string',
2974
- description: 'Price date (ISO 8601 format)'
3262
+ description: 'Base currency for converted values',
3263
+ example: 'CNY'
2975
3264
  },
2976
- metadata: {
2977
- type: 'object',
2978
- description: 'Metadata'
3265
+ byCurrency: {
3266
+ description:
3267
+ 'Multi-currency time series (each point has currency breakdown)',
3268
+ type: 'array',
3269
+ items: {
3270
+ $ref: '#/components/schemas/MultiCurrencyPointDto'
3271
+ }
3272
+ },
3273
+ warnings: {
3274
+ description: 'Exchange rate warnings',
3275
+ type: 'array',
3276
+ items: {
3277
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3278
+ }
2979
3279
  }
2980
- }
3280
+ },
3281
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
2981
3282
  } as const;
2982
3283
 
2983
- export const $CreateRecurringRuleDto = {
3284
+ export const $CashFlowPointDto = {
2984
3285
  type: 'object',
2985
3286
  properties: {
2986
- name: {
3287
+ month: {
2987
3288
  type: 'string',
2988
- description: 'Rule name (unique per user)',
2989
- maxLength: 100
3289
+ description: 'Month key (YYYY-MM)',
3290
+ example: '2024-03'
2990
3291
  },
2991
- icon: {
3292
+ income: {
2992
3293
  type: 'string',
2993
- description: 'Icon emoji',
2994
- maxLength: 10
3294
+ description: 'Income in base currency (absolute, converted)',
3295
+ example: '10000.00'
2995
3296
  },
2996
- frequency: {
3297
+ expense: {
2997
3298
  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
3299
+ description: 'Expense in base currency (absolute, converted)',
3300
+ example: '5000.00'
3019
3301
  },
3020
- customIntervalDays: {
3021
- type: 'number',
3022
- description: 'Custom interval in days (required for CUSTOM frequency)',
3023
- minimum: 1
3302
+ netSavings: {
3303
+ type: 'string',
3304
+ description: 'netSavings = income − expense (savings positive)',
3305
+ example: '5000.00'
3306
+ }
3307
+ },
3308
+ required: ['month', 'income', 'expense', 'netSavings']
3309
+ } as const;
3310
+
3311
+ export const $CashFlowTrendSummaryDto = {
3312
+ type: 'object',
3313
+ properties: {
3314
+ totalIncome: {
3315
+ type: 'string',
3316
+ description: 'Total income across the period',
3317
+ example: '60000.00'
3024
3318
  },
3025
- currency: {
3319
+ totalExpense: {
3026
3320
  type: 'string',
3027
- description: 'Currency code',
3028
- default: 'CNY',
3029
- maxLength: 10
3321
+ description: 'Total expense across the period',
3322
+ example: '30000.00'
3030
3323
  },
3031
- matchPayeePattern: {
3324
+ totalNetSavings: {
3032
3325
  type: 'string',
3033
- description: 'Payee matching pattern (supports wildcards)',
3034
- maxLength: 200
3326
+ description: 'income − expense across the period',
3327
+ example: '30000.00'
3035
3328
  },
3036
- matchAmountTolerance: {
3037
- type: 'number',
3038
- description: 'Amount tolerance percentage (0-1)',
3039
- default: 0.075,
3040
- minimum: 0,
3041
- maximum: 1
3042
- },
3043
- defaultExpenseAccount: {
3329
+ averageMonthlyNetSavings: {
3044
3330
  type: 'string',
3045
- description: 'Default expense account for auto-create',
3046
- maxLength: 200
3331
+ description:
3332
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3333
+ example: '5000.00'
3334
+ }
3335
+ },
3336
+ required: [
3337
+ 'totalIncome',
3338
+ 'totalExpense',
3339
+ 'totalNetSavings',
3340
+ 'averageMonthlyNetSavings'
3341
+ ]
3342
+ } as const;
3343
+
3344
+ export const $CashFlowTrendsResponseDto = {
3345
+ type: 'object',
3346
+ properties: {
3347
+ series: {
3348
+ description:
3349
+ 'Monthly cash-flow series (fixed N-month window, zero-filled)',
3350
+ type: 'array',
3351
+ items: {
3352
+ $ref: '#/components/schemas/CashFlowPointDto'
3353
+ }
3047
3354
  },
3048
- defaultPaymentAccount: {
3049
- type: 'string',
3050
- description: 'Default payment account for auto-create',
3051
- maxLength: 200
3355
+ summary: {
3356
+ description: 'Period totals',
3357
+ allOf: [
3358
+ {
3359
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
3360
+ }
3361
+ ]
3052
3362
  },
3053
- defaultPayee: {
3363
+ period: {
3054
3364
  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
3365
+ description: 'Period requested',
3366
+ example: '6m'
3062
3367
  },
3063
- startDate: {
3368
+ granularity: {
3064
3369
  type: 'string',
3065
- description: 'Rule start date (ISO format)'
3370
+ description: 'Data granularity (v1 returns month buckets)',
3371
+ example: 'month'
3066
3372
  },
3067
- endDate: {
3373
+ currency: {
3068
3374
  type: 'string',
3069
- description: 'Rule end date (ISO format)'
3375
+ description: 'Base currency for converted values',
3376
+ example: 'CNY'
3377
+ },
3378
+ warnings: {
3379
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
3380
+ type: 'array',
3381
+ items: {
3382
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3383
+ }
3070
3384
  }
3071
3385
  },
3072
- required: [
3073
- 'name',
3074
- 'frequency',
3075
- 'expectedAmount',
3076
- 'currency',
3077
- 'matchAmountTolerance',
3078
- 'autoCreate'
3079
- ]
3386
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3080
3387
  } as const;
3081
3388
 
3082
- export const $RecurringRuleResponseDto = {
3389
+ export const $GenerateSnapshotBody = {
3390
+ type: 'object',
3391
+ properties: {}
3392
+ } as const;
3393
+
3394
+ export const $GenerateSnapshotResponse = {
3395
+ type: 'object',
3396
+ properties: {}
3397
+ } as const;
3398
+
3399
+ export const $BackfillSnapshotsBody = {
3400
+ type: 'object',
3401
+ properties: {}
3402
+ } as const;
3403
+
3404
+ export const $BackfillSnapshotsResponse = {
3405
+ type: 'object',
3406
+ properties: {}
3407
+ } as const;
3408
+
3409
+ export const $CreateBeanPriceDto = {
3083
3410
  type: 'object',
3084
3411
  properties: {
3085
- id: {
3086
- type: 'string',
3087
- description: 'Rule ID'
3088
- },
3089
- userId: {
3412
+ currency: {
3090
3413
  type: 'string',
3091
- description: 'User ID'
3414
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3415
+ example: 'USD'
3092
3416
  },
3093
- name: {
3417
+ quoteCurrency: {
3094
3418
  type: 'string',
3095
- description: 'Rule name'
3419
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3420
+ example: 'CNY'
3096
3421
  },
3097
- icon: {
3098
- type: 'object',
3099
- description: 'Icon emoji'
3422
+ amount: {
3423
+ type: 'number',
3424
+ description:
3425
+ 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
3426
+ example: 175.5,
3427
+ minimum: 0
3100
3428
  },
3101
- frequency: {
3429
+ date: {
3102
3430
  type: 'string',
3103
- description: 'Recurring frequency'
3104
- },
3105
- expectedAmount: {
3106
- type: 'number',
3107
- description: 'Expected amount'
3431
+ description: 'Price date (ISO 8601 format)',
3432
+ example: '2024-11-05'
3108
3433
  },
3109
- expectedDay: {
3434
+ metadata: {
3110
3435
  type: 'object',
3111
- description: 'Expected day of month'
3436
+ description:
3437
+ 'Metadata (validated by Zod schema, max field lengths enforced)',
3438
+ example: {
3439
+ source: 'MANUAL',
3440
+ note: 'Bank valuation report',
3441
+ confidence: 0.95
3442
+ }
3443
+ }
3444
+ },
3445
+ required: ['currency', 'quoteCurrency', 'amount', 'date']
3446
+ } as const;
3447
+
3448
+ export const $PriceResponseDto = {
3449
+ type: 'object',
3450
+ properties: {
3451
+ id: {
3452
+ type: 'string',
3453
+ description: 'Unique identifier',
3454
+ example: 'uuid-123-456'
3112
3455
  },
3113
- customIntervalDays: {
3114
- type: 'object',
3115
- description: 'Custom interval in days'
3456
+ userId: {
3457
+ type: 'string',
3458
+ description: 'User ID (owner of the price)',
3459
+ example: 'user-123'
3116
3460
  },
3117
3461
  currency: {
3118
3462
  type: 'string',
3119
- description: 'Currency code'
3463
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3464
+ example: 'BTC'
3120
3465
  },
3121
- matchPayeePattern: {
3122
- type: 'object',
3123
- description: 'Payee matching pattern'
3466
+ quoteCurrency: {
3467
+ type: 'string',
3468
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
3469
+ example: 'USD'
3124
3470
  },
3125
- matchAmountTolerance: {
3471
+ amount: {
3126
3472
  type: 'number',
3127
- description: 'Amount tolerance percentage'
3128
- },
3129
- defaultExpenseAccount: {
3130
- type: 'object',
3131
- description: 'Default expense account'
3132
- },
3133
- defaultPaymentAccount: {
3134
- type: 'object',
3135
- description: 'Default payment account'
3136
- },
3137
- defaultPayee: {
3138
- type: 'object',
3139
- description: 'Default payee'
3140
- },
3141
- isActive: {
3142
- type: 'boolean',
3143
- description: 'Whether rule is active'
3473
+ description:
3474
+ 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
3475
+ example: 50000
3144
3476
  },
3145
- startDate: {
3477
+ date: {
3146
3478
  type: 'string',
3147
- description: 'Rule start date (YYYY-MM-DD)'
3148
- },
3149
- endDate: {
3150
- type: 'object',
3151
- description: 'Rule end date (YYYY-MM-DD)'
3152
- },
3153
- autoCreate: {
3154
- type: 'boolean',
3155
- description: 'Auto-create transaction on expected date'
3479
+ description:
3480
+ 'Price date (ISO 8601 format). Represents the date this price was valid.',
3481
+ example: '2024-01-01',
3482
+ format: 'date'
3156
3483
  },
3157
- lastOccurrence: {
3484
+ meta: {
3158
3485
  type: 'object',
3159
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3160
- },
3161
- totalCount: {
3162
- type: 'number',
3163
- description: 'Total matched transactions count'
3486
+ description:
3487
+ 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
3488
+ example: {
3489
+ source: 'MANUAL',
3490
+ note: 'User-defined price',
3491
+ confidence: 1
3492
+ }
3164
3493
  },
3165
3494
  createdAt: {
3166
3495
  format: 'date-time',
3167
3496
  type: 'string',
3168
- description: 'Created at timestamp'
3497
+ description: 'Creation timestamp',
3498
+ example: '2024-11-03T10:00:00Z'
3169
3499
  },
3170
3500
  updatedAt: {
3171
3501
  format: 'date-time',
3172
3502
  type: 'string',
3173
- description: 'Updated at timestamp'
3503
+ description: 'Last update timestamp',
3504
+ example: '2024-11-03T10:00:00Z'
3174
3505
  }
3175
3506
  },
3176
3507
  required: [
3177
3508
  'id',
3178
3509
  'userId',
3179
- 'name',
3180
- 'frequency',
3181
- 'expectedAmount',
3182
3510
  'currency',
3183
- 'matchAmountTolerance',
3184
- 'isActive',
3185
- 'startDate',
3186
- 'autoCreate',
3187
- 'totalCount',
3511
+ 'quoteCurrency',
3512
+ 'amount',
3513
+ 'date',
3514
+ 'meta',
3188
3515
  'createdAt',
3189
3516
  'updatedAt'
3190
3517
  ]
3191
3518
  } as const;
3192
3519
 
3193
- export const $CreateRuleFromTransactionDto = {
3520
+ export const $PriceListResponseDto = {
3194
3521
  type: 'object',
3195
3522
  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'
3523
+ items: {
3524
+ description: 'List of prices',
3525
+ type: 'array',
3526
+ items: {
3527
+ $ref: '#/components/schemas/PriceResponseDto'
3528
+ }
3209
3529
  },
3210
- name: {
3530
+ total: {
3531
+ type: 'number',
3532
+ description: 'Total number of prices',
3533
+ example: 42
3534
+ }
3535
+ },
3536
+ required: ['items', 'total']
3537
+ } as const;
3538
+
3539
+ export const $UpdateBeanPriceDto = {
3540
+ type: 'object',
3541
+ properties: {
3542
+ currency: {
3211
3543
  type: 'string',
3212
- description: 'Optional name override (default: transaction payee)',
3213
- maxLength: 100
3544
+ description: 'Currency being priced'
3214
3545
  },
3215
- icon: {
3546
+ quoteCurrency: {
3216
3547
  type: 'string',
3217
- description: 'Optional icon emoji',
3218
- maxLength: 10
3548
+ description: 'Quote currency (pricing currency)'
3549
+ },
3550
+ amount: {
3551
+ type: 'number',
3552
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
3553
+ minimum: 0
3554
+ },
3555
+ date: {
3556
+ type: 'string',
3557
+ description: 'Price date (ISO 8601 format)'
3558
+ },
3559
+ metadata: {
3560
+ type: 'object',
3561
+ description: 'Metadata'
3562
+ }
3563
+ }
3564
+ } as const;
3565
+
3566
+ export const $DeleteOwnUserDto = {
3567
+ type: 'object',
3568
+ properties: {
3569
+ accessToken: {
3570
+ type: 'string',
3571
+ description: 'Access token for user verification',
3572
+ example: 'abc123xyz'
3219
3573
  }
3220
3574
  },
3221
- required: ['frequency']
3575
+ required: ['accessToken']
3222
3576
  } as const;
3223
3577
 
3224
- export const $RecurringRuleWithStatsResponseDto = {
3578
+ export const $UserSettingsResponseDto = {
3579
+ type: 'object',
3580
+ properties: {
3581
+ baseCurrency: {
3582
+ type: 'string',
3583
+ description:
3584
+ '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).',
3585
+ example: 'USD',
3586
+ nullable: true
3587
+ }
3588
+ },
3589
+ required: ['baseCurrency']
3590
+ } as const;
3591
+
3592
+ export const $UserResponseDto = {
3225
3593
  type: 'object',
3226
3594
  properties: {
3227
3595
  id: {
3228
3596
  type: 'string',
3229
- description: 'Rule ID'
3597
+ description: 'User ID'
3230
3598
  },
3231
- userId: {
3599
+ role: {
3232
3600
  type: 'string',
3233
- description: 'User ID'
3601
+ description: 'Assigned user role'
3234
3602
  },
3235
- name: {
3603
+ permissions: {
3604
+ description: 'Permission strings',
3605
+ type: 'array',
3606
+ items: {
3607
+ type: 'string'
3608
+ }
3609
+ },
3610
+ settings: {
3611
+ description: 'User settings',
3612
+ allOf: [
3613
+ {
3614
+ $ref: '#/components/schemas/UserSettingsResponseDto'
3615
+ }
3616
+ ]
3617
+ }
3618
+ },
3619
+ required: ['id', 'role', 'permissions', 'settings']
3620
+ } as const;
3621
+
3622
+ export const $SignupDto = {
3623
+ type: 'object',
3624
+ properties: {
3625
+ turnstileToken: {
3236
3626
  type: 'string',
3237
- description: 'Rule name'
3627
+ description:
3628
+ 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
3629
+ example: '0.abc123def456...'
3630
+ }
3631
+ }
3632
+ } as const;
3633
+
3634
+ export const $SignupResponseDto = {
3635
+ type: 'object',
3636
+ properties: {
3637
+ authToken: {
3638
+ type: 'string',
3639
+ description: 'JWT auth token',
3640
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
3238
3641
  },
3239
- icon: {
3240
- type: 'object',
3241
- description: 'Icon emoji'
3642
+ accessToken: {
3643
+ type: 'string',
3644
+ description: 'Auto-generated access token'
3242
3645
  },
3243
- frequency: {
3646
+ role: {
3244
3647
  type: 'string',
3245
- description: 'Recurring frequency'
3648
+ description: 'Assigned user role',
3649
+ enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
3650
+ }
3651
+ },
3652
+ required: ['authToken', 'accessToken', 'role']
3653
+ } as const;
3654
+
3655
+ export const $UpdateUserSettingDto = {
3656
+ type: 'object',
3657
+ properties: {
3658
+ secId: {
3659
+ type: 'number',
3660
+ description: 'Security ID'
3246
3661
  },
3247
- expectedAmount: {
3662
+ annualInterestRate: {
3248
3663
  type: 'number',
3249
- description: 'Expected amount'
3664
+ description: 'Annual interest rate',
3665
+ example: 0.05
3250
3666
  },
3251
- expectedDay: {
3252
- type: 'object',
3253
- description: 'Expected day of month'
3667
+ currency: {
3668
+ type: 'string',
3669
+ description: 'Currency code',
3670
+ example: 'USD'
3254
3671
  },
3255
- customIntervalDays: {
3256
- type: 'object',
3257
- description: 'Custom interval in days'
3672
+ baseCurrency: {
3673
+ type: 'string',
3674
+ description: 'Base currency code',
3675
+ example: 'USD'
3258
3676
  },
3259
- currency: {
3677
+ benchmark: {
3260
3678
  type: 'string',
3261
- description: 'Currency code'
3679
+ description: 'Benchmark symbol',
3680
+ example: 'SPY'
3262
3681
  },
3263
- matchPayeePattern: {
3264
- type: 'object',
3265
- description: 'Payee matching pattern'
3682
+ colorScheme: {
3683
+ type: 'string',
3684
+ description: 'Color scheme',
3685
+ enum: ['DARK', 'LIGHT']
3266
3686
  },
3267
- matchAmountTolerance: {
3268
- type: 'number',
3269
- description: 'Amount tolerance percentage'
3687
+ dateRange: {
3688
+ type: 'string',
3689
+ description: 'Date range filter',
3690
+ example: '1y'
3270
3691
  },
3271
- defaultExpenseAccount: {
3272
- type: 'object',
3273
- description: 'Default expense account'
3692
+ emergencyFund: {
3693
+ type: 'number',
3694
+ description: 'Emergency fund amount',
3695
+ example: 10000
3274
3696
  },
3275
- defaultPaymentAccount: {
3276
- type: 'object',
3277
- description: 'Default payment account'
3697
+ 'filters.accounts': {
3698
+ description: 'Account filter IDs',
3699
+ type: 'array',
3700
+ items: {
3701
+ type: 'string'
3702
+ }
3278
3703
  },
3279
- defaultPayee: {
3280
- type: 'object',
3281
- description: 'Default payee'
3704
+ 'filters.assetClasses': {
3705
+ description: 'Asset class filters',
3706
+ type: 'array',
3707
+ items: {
3708
+ type: 'string'
3709
+ }
3282
3710
  },
3283
- isActive: {
3284
- type: 'boolean',
3285
- description: 'Whether rule is active'
3711
+ 'filters.dataSource': {
3712
+ type: 'string',
3713
+ description: 'Data source filter'
3286
3714
  },
3287
- startDate: {
3715
+ 'filters.symbol': {
3288
3716
  type: 'string',
3289
- description: 'Rule start date (YYYY-MM-DD)'
3717
+ description: 'Symbol filter'
3290
3718
  },
3291
- endDate: {
3292
- type: 'object',
3293
- description: 'Rule end date (YYYY-MM-DD)'
3719
+ 'filters.tags': {
3720
+ description: 'Tag filters',
3721
+ type: 'array',
3722
+ items: {
3723
+ type: 'string'
3724
+ }
3294
3725
  },
3295
- autoCreate: {
3726
+ isExperimentalFeatures: {
3296
3727
  type: 'boolean',
3297
- description: 'Auto-create transaction on expected date'
3298
- },
3299
- lastOccurrence: {
3300
- type: 'object',
3301
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3728
+ description: 'Enable experimental features'
3302
3729
  },
3303
- totalCount: {
3304
- type: 'number',
3305
- description: 'Total matched transactions count'
3730
+ isRestrictedView: {
3731
+ type: 'boolean',
3732
+ description: 'Enable restricted view mode'
3306
3733
  },
3307
- createdAt: {
3308
- format: 'date-time',
3734
+ language: {
3309
3735
  type: 'string',
3310
- description: 'Created at timestamp'
3736
+ description: 'Language code',
3737
+ example: 'en'
3311
3738
  },
3312
- updatedAt: {
3313
- format: 'date-time',
3739
+ locale: {
3314
3740
  type: 'string',
3315
- description: 'Updated at timestamp'
3316
- },
3317
- pendingCount: {
3318
- type: 'number',
3319
- description: 'Number of pending expected transactions'
3741
+ description: 'Locale code',
3742
+ example: 'en-US'
3320
3743
  },
3321
- overdueCount: {
3744
+ projectedTotalAmount: {
3322
3745
  type: 'number',
3323
- description: 'Number of overdue expected transactions'
3746
+ description: 'Projected total amount',
3747
+ example: 1000000
3324
3748
  },
3325
- nextExpectedDate: {
3326
- type: 'object',
3327
- description: 'Next expected date (YYYY-MM-DD)'
3749
+ retirementDate: {
3750
+ type: 'string',
3751
+ description: 'Retirement date in ISO 8601 format',
3752
+ example: '2050-01-01'
3328
3753
  },
3329
- totalAmount: {
3754
+ savingsRate: {
3330
3755
  type: 'number',
3331
- description: 'Total amount of all matched transactions'
3756
+ description: 'Savings rate percentage',
3757
+ example: 0.2
3332
3758
  },
3333
- averageAmount: {
3334
- type: 'number',
3335
- description: 'Average amount per transaction'
3336
- },
3337
- transactionCount: {
3338
- type: 'number',
3339
- description: 'Number of matched transactions'
3340
- },
3341
- firstDate: {
3342
- type: 'object',
3343
- description: 'First matched transaction date (YYYY-MM-DD)'
3344
- },
3345
- lastDate: {
3346
- type: 'object',
3347
- description: 'Last matched transaction date (YYYY-MM-DD)'
3348
- },
3349
- variance: {
3350
- type: 'number',
3351
- description: 'Amount variance (standard deviation squared)'
3352
- },
3353
- upcomingCount: {
3354
- type: 'number',
3355
- description: 'Number of upcoming expected transactions'
3759
+ viewMode: {
3760
+ type: 'string',
3761
+ description: 'View mode',
3762
+ enum: ['DEFAULT', 'ZEN']
3763
+ }
3764
+ }
3765
+ } as const;
3766
+
3767
+ export const $UpdatePropertyDto = {
3768
+ type: 'object',
3769
+ properties: {
3770
+ value: {
3771
+ type: 'string',
3772
+ description: 'Property value'
3356
3773
  }
3357
3774
  },
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
- ]
3775
+ required: ['value']
3380
3776
  } as const;
3381
3777
 
3382
- export const $UpdateRecurringRuleDto = {
3778
+ export const $CreateRecurringRuleDto = {
3383
3779
  type: 'object',
3384
3780
  properties: {
3385
3781
  name: {
3386
3782
  type: 'string',
3387
- description: 'Rule name',
3783
+ description: 'Rule name (unique per user)',
3388
3784
  maxLength: 100
3389
3785
  },
3390
3786
  icon: {
@@ -3407,7 +3803,7 @@ export const $UpdateRecurringRuleDto = {
3407
3803
  },
3408
3804
  expectedAmount: {
3409
3805
  type: 'number',
3410
- description: 'Expected amount',
3806
+ description: 'Expected amount (positive number)',
3411
3807
  minimum: 0
3412
3808
  },
3413
3809
  expectedDay: {
@@ -3418,7 +3814,7 @@ export const $UpdateRecurringRuleDto = {
3418
3814
  },
3419
3815
  customIntervalDays: {
3420
3816
  type: 'number',
3421
- description: 'Custom interval in days',
3817
+ description: 'Custom interval in days (required for CUSTOM frequency)',
3422
3818
  minimum: 1
3423
3819
  },
3424
3820
  currency: {
@@ -3428,118 +3824,136 @@ export const $UpdateRecurringRuleDto = {
3428
3824
  },
3429
3825
  matchPayeePattern: {
3430
3826
  type: 'string',
3431
- description: 'Payee matching pattern',
3827
+ description: 'Payee matching pattern (supports wildcards)',
3432
3828
  maxLength: 200
3433
3829
  },
3434
3830
  matchAmountTolerance: {
3435
3831
  type: 'number',
3436
3832
  description: 'Amount tolerance percentage (0-1)',
3833
+ default: 0.075,
3437
3834
  minimum: 0,
3438
3835
  maximum: 1
3439
3836
  },
3440
3837
  defaultExpenseAccount: {
3441
3838
  type: 'string',
3442
- description: 'Default expense account',
3839
+ description: 'Default expense account for auto-create',
3443
3840
  maxLength: 200
3444
3841
  },
3445
3842
  defaultPaymentAccount: {
3446
3843
  type: 'string',
3447
- description: 'Default payment account',
3844
+ description: 'Default payment account for auto-create',
3448
3845
  maxLength: 200
3449
3846
  },
3450
3847
  defaultPayee: {
3451
3848
  type: 'string',
3452
- description: 'Default payee',
3849
+ description: 'Default payee for auto-create',
3453
3850
  maxLength: 200
3454
3851
  },
3455
3852
  autoCreate: {
3456
3853
  type: 'boolean',
3457
- description: 'Auto-create transaction'
3854
+ description: 'Auto-create transaction when expected date arrives',
3855
+ default: false
3458
3856
  },
3459
- isActive: {
3460
- type: 'boolean',
3461
- description: 'Rule active status'
3857
+ startDate: {
3858
+ type: 'string',
3859
+ description: 'Rule start date (ISO format)'
3462
3860
  },
3463
3861
  endDate: {
3464
3862
  type: 'string',
3465
3863
  description: 'Rule end date (ISO format)'
3466
3864
  }
3467
- }
3865
+ },
3866
+ required: [
3867
+ 'name',
3868
+ 'frequency',
3869
+ 'expectedAmount',
3870
+ 'matchAmountTolerance',
3871
+ 'autoCreate'
3872
+ ]
3468
3873
  } as const;
3469
3874
 
3470
- export const $ExpectedTransactionRuleDto = {
3875
+ export const $RecurringRuleResponseDto = {
3471
3876
  type: 'object',
3472
3877
  properties: {
3878
+ id: {
3879
+ type: 'string',
3880
+ description: 'Rule ID'
3881
+ },
3882
+ userId: {
3883
+ type: 'string',
3884
+ description: 'User ID'
3885
+ },
3473
3886
  name: {
3474
3887
  type: 'string',
3475
3888
  description: 'Rule name'
3476
3889
  },
3477
3890
  icon: {
3478
- type: 'object',
3479
- description: 'Rule icon'
3891
+ type: 'string',
3892
+ description: 'Icon emoji'
3480
3893
  },
3481
3894
  frequency: {
3482
3895
  type: 'string',
3483
- description: 'Rule frequency'
3896
+ description: 'Recurring frequency'
3897
+ },
3898
+ expectedAmount: {
3899
+ type: 'number',
3900
+ description: 'Expected amount'
3901
+ },
3902
+ expectedDay: {
3903
+ type: 'number',
3904
+ description: 'Expected day of month'
3905
+ },
3906
+ customIntervalDays: {
3907
+ type: 'number',
3908
+ description: 'Custom interval in days'
3484
3909
  },
3485
3910
  currency: {
3486
3911
  type: 'string',
3487
3912
  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
3913
  },
3500
- userId: {
3914
+ matchPayeePattern: {
3501
3915
  type: 'string',
3502
- description: 'User ID'
3916
+ description: 'Payee matching pattern'
3503
3917
  },
3504
- ruleId: {
3505
- type: 'string',
3506
- description: 'Associated rule ID'
3918
+ matchAmountTolerance: {
3919
+ type: 'number',
3920
+ description: 'Amount tolerance percentage'
3507
3921
  },
3508
- expectedDate: {
3922
+ defaultExpenseAccount: {
3509
3923
  type: 'string',
3510
- description: 'Expected date (YYYY-MM-DD)'
3924
+ description: 'Default expense account'
3511
3925
  },
3512
- expectedAmount: {
3513
- type: 'number',
3514
- description: 'Expected amount'
3926
+ defaultPaymentAccount: {
3927
+ type: 'string',
3928
+ description: 'Default payment account'
3515
3929
  },
3516
- status: {
3930
+ defaultPayee: {
3517
3931
  type: 'string',
3518
- description: 'Status (PENDING, COMPLETED, SKIPPED)'
3932
+ description: 'Default payee'
3519
3933
  },
3520
- matchedTransactionId: {
3521
- type: 'object',
3522
- description: 'Matched transaction ID'
3934
+ isActive: {
3935
+ type: 'boolean',
3936
+ description: 'Whether rule is active'
3523
3937
  },
3524
- matchedAt: {
3525
- type: 'object',
3526
- description: 'Match timestamp (ISO 8601)'
3938
+ startDate: {
3939
+ type: 'string',
3940
+ description: 'Rule start date (YYYY-MM-DD)'
3527
3941
  },
3528
- matchConfidence: {
3529
- type: 'object',
3530
- description: 'Match confidence score (0-1)'
3942
+ endDate: {
3943
+ type: 'string',
3944
+ description: 'Rule end date (YYYY-MM-DD)'
3531
3945
  },
3532
- isOverdue: {
3946
+ autoCreate: {
3533
3947
  type: 'boolean',
3534
- description: 'Whether this expected transaction is overdue'
3948
+ description: 'Auto-create transaction on expected date'
3535
3949
  },
3536
- rule: {
3537
- description: 'Rule information',
3538
- allOf: [
3539
- {
3540
- $ref: '#/components/schemas/ExpectedTransactionRuleDto'
3541
- }
3542
- ]
3950
+ lastOccurrence: {
3951
+ type: 'string',
3952
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
3953
+ },
3954
+ totalCount: {
3955
+ type: 'number',
3956
+ description: 'Total matched transactions count'
3543
3957
  },
3544
3958
  createdAt: {
3545
3959
  format: 'date-time',
@@ -3555,647 +3969,581 @@ export const $ExpectedTransactionResponseDto = {
3555
3969
  required: [
3556
3970
  'id',
3557
3971
  'userId',
3558
- 'ruleId',
3559
- 'expectedDate',
3972
+ 'name',
3973
+ 'frequency',
3560
3974
  'expectedAmount',
3561
- 'status',
3562
- 'isOverdue',
3563
- 'rule',
3975
+ 'currency',
3976
+ 'matchAmountTolerance',
3977
+ 'isActive',
3978
+ 'startDate',
3979
+ 'autoCreate',
3980
+ 'totalCount',
3564
3981
  'createdAt',
3565
3982
  'updatedAt'
3566
3983
  ]
3567
3984
  } as const;
3568
3985
 
3569
- export const $ExpectedTransactionListResponseDto = {
3986
+ export const $CreateRuleFromTransactionDto = {
3570
3987
  type: 'object',
3571
3988
  properties: {
3572
- items: {
3573
- type: 'array',
3574
- items: {
3575
- $ref: '#/components/schemas/ExpectedTransactionResponseDto'
3576
- }
3989
+ frequency: {
3990
+ type: 'string',
3991
+ description: 'Recurring frequency',
3992
+ enum: [
3993
+ 'WEEKLY',
3994
+ 'BIWEEKLY',
3995
+ 'MONTHLY',
3996
+ 'BIMONTHLY',
3997
+ 'QUARTERLY',
3998
+ 'YEARLY',
3999
+ 'CUSTOM'
4000
+ ],
4001
+ example: 'MONTHLY'
3577
4002
  },
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: {
4003
+ name: {
3590
4004
  type: 'string',
3591
- description: 'Transaction ID to match with'
4005
+ description: 'Optional name override (default: transaction payee)',
4006
+ maxLength: 100
4007
+ },
4008
+ icon: {
4009
+ type: 'string',
4010
+ description: 'Optional icon emoji',
4011
+ maxLength: 10
3592
4012
  }
3593
4013
  },
3594
- required: ['transactionId']
4014
+ required: ['frequency']
3595
4015
  } as const;
3596
4016
 
3597
- export const $EnterNowDto = {
4017
+ export const $RecurringRuleWithStatsResponseDto = {
3598
4018
  type: 'object',
3599
4019
  properties: {
3600
- expenseAccount: {
4020
+ id: {
3601
4021
  type: 'string',
3602
- description:
3603
- 'Override expense account (uses rule default if not provided)',
3604
- maxLength: 200
4022
+ description: 'Rule ID'
3605
4023
  },
3606
- paymentAccount: {
4024
+ userId: {
3607
4025
  type: 'string',
3608
- description:
3609
- 'Override payment account (uses rule default if not provided)',
3610
- maxLength: 200
4026
+ description: 'User ID'
3611
4027
  },
3612
- amount: {
3613
- type: 'number',
3614
- description: 'Override amount (uses expected amount if not provided)',
3615
- minimum: 0
4028
+ name: {
4029
+ type: 'string',
4030
+ description: 'Rule name'
3616
4031
  },
3617
- payee: {
4032
+ icon: {
3618
4033
  type: 'string',
3619
- description: 'Override payee (uses rule default if not provided)',
3620
- maxLength: 200
4034
+ description: 'Icon emoji'
3621
4035
  },
3622
- narration: {
4036
+ frequency: {
3623
4037
  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: {
4038
+ description: 'Recurring frequency'
4039
+ },
4040
+ expectedAmount: {
4041
+ type: 'number',
4042
+ description: 'Expected amount'
4043
+ },
4044
+ expectedDay: {
4045
+ type: 'number',
4046
+ description: 'Expected day of month'
4047
+ },
4048
+ customIntervalDays: {
4049
+ type: 'number',
4050
+ description: 'Custom interval in days'
4051
+ },
4052
+ currency: {
3634
4053
  type: 'string',
3635
- description: 'Rule name',
3636
- example: 'Rent'
4054
+ description: 'Currency code'
3637
4055
  },
3638
- ruleId: {
4056
+ matchPayeePattern: {
3639
4057
  type: 'string',
3640
- description: 'Rule ID',
3641
- example: 'clx123...'
4058
+ description: 'Payee matching pattern'
3642
4059
  },
3643
- amount: {
4060
+ matchAmountTolerance: {
3644
4061
  type: 'number',
3645
- description: 'Expected amount',
3646
- example: 3000
4062
+ description: 'Amount tolerance percentage'
3647
4063
  },
3648
- date: {
4064
+ defaultExpenseAccount: {
3649
4065
  type: 'string',
3650
- description: 'Expected date (YYYY-MM-DD)',
3651
- example: '2024-04-01'
4066
+ description: 'Default expense account'
3652
4067
  },
3653
- icon: {
4068
+ defaultPaymentAccount: {
3654
4069
  type: 'string',
3655
- description: 'Rule icon emoji',
3656
- example: '🏠',
3657
- nullable: true
4070
+ description: 'Default payment account'
3658
4071
  },
3659
- currency: {
4072
+ defaultPayee: {
3660
4073
  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: {
4074
+ description: 'Default payee'
4075
+ },
4076
+ isActive: {
4077
+ type: 'boolean',
4078
+ description: 'Whether rule is active'
4079
+ },
4080
+ startDate: {
3672
4081
  type: 'string',
3673
- description: 'Month (YYYY-MM)',
3674
- example: '2024-04'
4082
+ description: 'Rule start date (YYYY-MM-DD)'
3675
4083
  },
3676
- expectedOutflow: {
4084
+ endDate: {
4085
+ type: 'string',
4086
+ description: 'Rule end date (YYYY-MM-DD)'
4087
+ },
4088
+ autoCreate: {
4089
+ type: 'boolean',
4090
+ description: 'Auto-create transaction on expected date'
4091
+ },
4092
+ lastOccurrence: {
4093
+ type: 'string',
4094
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
4095
+ },
4096
+ totalCount: {
3677
4097
  type: 'number',
3678
- description: 'Total expected outflow for the month',
3679
- example: 8500
4098
+ description: 'Total matched transactions count'
3680
4099
  },
3681
- itemCount: {
4100
+ createdAt: {
4101
+ format: 'date-time',
4102
+ type: 'string',
4103
+ description: 'Created at timestamp'
4104
+ },
4105
+ updatedAt: {
4106
+ format: 'date-time',
4107
+ type: 'string',
4108
+ description: 'Updated at timestamp'
4109
+ },
4110
+ pendingCount: {
3682
4111
  type: 'number',
3683
- description: 'Number of expected transactions',
3684
- example: 3
4112
+ description: 'Number of pending expected transactions'
3685
4113
  },
3686
- byCurrency: {
3687
- type: 'object',
3688
- description: 'Breakdown by currency',
3689
- example: {
3690
- CNY: 8500,
3691
- USD: 100
3692
- }
4114
+ overdueCount: {
4115
+ type: 'number',
4116
+ description: 'Number of overdue expected transactions'
3693
4117
  },
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
- }
4118
+ nextExpectedDate: {
4119
+ type: 'string',
4120
+ description: 'Next expected date (YYYY-MM-DD)'
3714
4121
  },
3715
- totalOutflow: {
4122
+ totalAmount: {
3716
4123
  type: 'number',
3717
- description: 'Total expected outflow across all months',
3718
- example: 25500
4124
+ description: 'Total amount of all matched transactions'
3719
4125
  },
3720
- totalByCurrency: {
3721
- type: 'object',
3722
- description: 'Total by currency across all months',
3723
- example: {
3724
- CNY: 25500,
3725
- USD: 300
3726
- }
4126
+ averageAmount: {
4127
+ type: 'number',
4128
+ description: 'Average amount per transaction'
3727
4129
  },
3728
- rulesCount: {
4130
+ transactionCount: {
3729
4131
  type: 'number',
3730
- description: 'Number of active recurring rules included',
3731
- example: 5
4132
+ description: 'Number of matched transactions'
3732
4133
  },
3733
- periodStart: {
4134
+ firstDate: {
3734
4135
  type: 'string',
3735
- description: 'Forecast period start date',
3736
- example: '2024-04-01'
4136
+ description: 'First matched transaction date (YYYY-MM-DD)'
3737
4137
  },
3738
- periodEnd: {
4138
+ lastDate: {
3739
4139
  type: 'string',
3740
- description: 'Forecast period end date',
3741
- example: '2024-06-30'
4140
+ description: 'Last matched transaction date (YYYY-MM-DD)'
4141
+ },
4142
+ variance: {
4143
+ type: 'number',
4144
+ description: 'Amount variance (standard deviation squared)'
4145
+ },
4146
+ upcomingCount: {
4147
+ type: 'number',
4148
+ description: 'Number of upcoming expected transactions'
3742
4149
  }
3743
4150
  },
3744
4151
  required: [
3745
- 'forecast',
3746
- 'totalOutflow',
3747
- 'totalByCurrency',
3748
- 'rulesCount',
3749
- 'periodStart',
3750
- 'periodEnd'
4152
+ 'id',
4153
+ 'userId',
4154
+ 'name',
4155
+ 'frequency',
4156
+ 'expectedAmount',
4157
+ 'currency',
4158
+ 'matchAmountTolerance',
4159
+ 'isActive',
4160
+ 'startDate',
4161
+ 'autoCreate',
4162
+ 'totalCount',
4163
+ 'createdAt',
4164
+ 'updatedAt',
4165
+ 'pendingCount',
4166
+ 'overdueCount',
4167
+ 'totalAmount',
4168
+ 'averageAmount',
4169
+ 'transactionCount',
4170
+ 'variance',
4171
+ 'upcomingCount'
3751
4172
  ]
3752
4173
  } as const;
3753
4174
 
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 = {
4175
+ export const $UpdateRecurringRuleDto = {
3772
4176
  type: 'object',
3773
4177
  properties: {
3774
- date: {
3775
- type: 'string',
3776
- description: 'Date in YYYY-MM-DD format',
3777
- example: '2024-06-15'
3778
- },
3779
- value: {
4178
+ name: {
3780
4179
  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'
4180
+ description: 'Rule name (unique per user)',
4181
+ maxLength: 100
3788
4182
  },
3789
- assets: {
4183
+ icon: {
3790
4184
  type: 'string',
3791
- description: 'Total assets at this date (in base currency)',
3792
- example: '494338.00'
4185
+ description: 'Icon emoji',
4186
+ maxLength: 10
3793
4187
  },
3794
- liabilities: {
4188
+ frequency: {
3795
4189
  type: 'string',
3796
- description: 'Total liabilities at this date (in base currency)',
3797
- example: '310098.00'
4190
+ description: 'Recurring frequency',
4191
+ enum: [
4192
+ 'WEEKLY',
4193
+ 'BIWEEKLY',
4194
+ 'MONTHLY',
4195
+ 'BIMONTHLY',
4196
+ 'QUARTERLY',
4197
+ 'YEARLY',
4198
+ 'CUSTOM'
4199
+ ]
3798
4200
  },
3799
- byCurrency: {
3800
- description: 'Multi-currency breakdown for this point',
3801
- type: 'array',
3802
- items: {
3803
- $ref: '#/components/schemas/CurrencyBalanceDto'
3804
- }
3805
- }
3806
- },
3807
- required: ['date', 'value']
3808
- } as const;
3809
-
3810
- export const $TrendSummaryDto = {
3811
- type: 'object',
3812
- properties: {
3813
- startValue: {
3814
- type: 'string',
3815
- description: 'Value at start of period',
3816
- example: '450000.00'
4201
+ expectedAmount: {
4202
+ type: 'number',
4203
+ description: 'Expected amount (positive number)',
4204
+ minimum: 0
3817
4205
  },
3818
- endValue: {
3819
- type: 'string',
3820
- description: 'Value at end of period',
3821
- example: '500000.00'
4206
+ expectedDay: {
4207
+ type: 'number',
4208
+ description: 'Expected day of month (1-31)',
4209
+ minimum: 1,
4210
+ maximum: 31
3822
4211
  },
3823
- totalChange: {
4212
+ currency: {
3824
4213
  type: 'string',
3825
- description: 'Total change over period',
3826
- example: '50000.00'
4214
+ description: 'Currency code',
4215
+ maxLength: 10
3827
4216
  },
3828
- totalChangePercentage: {
3829
- type: 'string',
3830
- description: 'Total change percentage',
3831
- example: '+11.11%'
3832
- }
3833
- },
3834
- required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
3835
- } as const;
3836
-
3837
- export const $MultiCurrencyPointDto = {
3838
- type: 'object',
3839
- properties: {
3840
- date: {
4217
+ matchPayeePattern: {
3841
4218
  type: 'string',
3842
- description: 'Date in YYYY-MM-DD format',
3843
- example: '2024-06-15'
3844
- },
3845
- byCurrency: {
3846
- description: 'Balances by currency',
3847
- type: 'array',
3848
- items: {
3849
- $ref: '#/components/schemas/CurrencyBalanceDto'
3850
- }
3851
- }
3852
- },
3853
- required: ['date', 'byCurrency']
3854
- } as const;
3855
-
3856
- export const $PortfolioTrendsResponseDto = {
3857
- type: 'object',
3858
- properties: {
3859
- series: {
3860
- description: 'Time series data points',
3861
- type: 'array',
3862
- items: {
3863
- $ref: '#/components/schemas/TimeSeriesPointDto'
3864
- }
4219
+ description: 'Payee matching pattern (supports wildcards)',
4220
+ maxLength: 200
3865
4221
  },
3866
- summary: {
3867
- description: 'Period summary',
3868
- allOf: [
3869
- {
3870
- $ref: '#/components/schemas/TrendSummaryDto'
3871
- }
3872
- ]
4222
+ matchAmountTolerance: {
4223
+ type: 'number',
4224
+ description: 'Amount tolerance percentage (0-1)',
4225
+ default: 0.075,
4226
+ minimum: 0,
4227
+ maximum: 1
3873
4228
  },
3874
- period: {
4229
+ defaultExpenseAccount: {
3875
4230
  type: 'string',
3876
- description: 'Period requested',
3877
- example: '6m'
4231
+ description: 'Default expense account for auto-create',
4232
+ maxLength: 200
3878
4233
  },
3879
- granularity: {
4234
+ defaultPaymentAccount: {
3880
4235
  type: 'string',
3881
- description: 'Data granularity',
3882
- example: 'month'
4236
+ description: 'Default payment account for auto-create',
4237
+ maxLength: 200
3883
4238
  },
3884
- currency: {
4239
+ defaultPayee: {
3885
4240
  type: 'string',
3886
- description: 'Base currency for converted values',
3887
- example: 'CNY'
4241
+ description: 'Default payee for auto-create',
4242
+ maxLength: 200
3888
4243
  },
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
- }
4244
+ autoCreate: {
4245
+ type: 'boolean',
4246
+ description: 'Auto-create transaction when expected date arrives',
4247
+ default: false
3896
4248
  },
3897
- warnings: {
3898
- description: 'Exchange rate warnings',
3899
- type: 'array',
3900
- items: {
3901
- $ref: '#/components/schemas/ExchangeRateWarningDto'
3902
- }
4249
+ endDate: {
4250
+ type: 'string',
4251
+ description: 'Rule end date (ISO format)'
4252
+ },
4253
+ customIntervalDays: {
4254
+ type: 'number',
4255
+ description: 'Custom interval in days',
4256
+ minimum: 1
4257
+ },
4258
+ isActive: {
4259
+ type: 'boolean',
4260
+ description: 'Rule active status'
3903
4261
  }
3904
- },
3905
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4262
+ }
3906
4263
  } as const;
3907
4264
 
3908
- export const $CashFlowPointDto = {
4265
+ export const $ExpectedTransactionRuleDto = {
3909
4266
  type: 'object',
3910
4267
  properties: {
3911
- month: {
4268
+ name: {
3912
4269
  type: 'string',
3913
- description: 'Month key (YYYY-MM)',
3914
- example: '2024-03'
4270
+ description: 'Rule name'
3915
4271
  },
3916
- income: {
4272
+ icon: {
3917
4273
  type: 'string',
3918
- description: 'Income in base currency (absolute, converted)',
3919
- example: '10000.00'
4274
+ description: 'Rule icon'
3920
4275
  },
3921
- expense: {
4276
+ frequency: {
3922
4277
  type: 'string',
3923
- description: 'Expense in base currency (absolute, converted)',
3924
- example: '5000.00'
4278
+ description: 'Rule frequency'
3925
4279
  },
3926
- netSavings: {
4280
+ currency: {
3927
4281
  type: 'string',
3928
- description: 'netSavings = income − expense (savings positive)',
3929
- example: '5000.00'
4282
+ description: 'Currency code'
3930
4283
  }
3931
4284
  },
3932
- required: ['month', 'income', 'expense', 'netSavings']
4285
+ required: ['name', 'frequency', 'currency']
3933
4286
  } as const;
3934
4287
 
3935
- export const $CashFlowTrendSummaryDto = {
4288
+ export const $ExpectedTransactionResponseDto = {
3936
4289
  type: 'object',
3937
4290
  properties: {
3938
- totalIncome: {
4291
+ id: {
3939
4292
  type: 'string',
3940
- description: 'Total income across the period',
3941
- example: '60000.00'
4293
+ description: 'Expected transaction ID'
3942
4294
  },
3943
- totalExpense: {
4295
+ userId: {
3944
4296
  type: 'string',
3945
- description: 'Total expense across the period',
3946
- example: '30000.00'
4297
+ description: 'User ID'
3947
4298
  },
3948
- totalNetSavings: {
4299
+ ruleId: {
3949
4300
  type: 'string',
3950
- description: 'income − expense across the period',
3951
- example: '30000.00'
4301
+ description: 'Associated rule ID'
3952
4302
  },
3953
- averageMonthlyNetSavings: {
4303
+ expectedDate: {
3954
4304
  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
- }
4305
+ description: 'Expected date (YYYY-MM-DD)'
3978
4306
  },
3979
- summary: {
3980
- description: 'Period totals',
4307
+ expectedAmount: {
4308
+ type: 'number',
4309
+ description: 'Expected amount'
4310
+ },
4311
+ status: {
4312
+ type: 'string',
4313
+ description: 'Status (PENDING, COMPLETED, SKIPPED)'
4314
+ },
4315
+ matchedTransactionId: {
4316
+ type: 'string',
4317
+ description: 'Matched transaction ID'
4318
+ },
4319
+ matchedAt: {
4320
+ type: 'string',
4321
+ description: 'Match timestamp (ISO 8601)'
4322
+ },
4323
+ matchConfidence: {
4324
+ type: 'number',
4325
+ description: 'Match confidence score (0-1)'
4326
+ },
4327
+ isOverdue: {
4328
+ type: 'boolean',
4329
+ description: 'Whether this expected transaction is overdue'
4330
+ },
4331
+ rule: {
4332
+ description: 'Rule information',
3981
4333
  allOf: [
3982
4334
  {
3983
- $ref: '#/components/schemas/CashFlowTrendSummaryDto'
4335
+ $ref: '#/components/schemas/ExpectedTransactionRuleDto'
3984
4336
  }
3985
4337
  ]
3986
4338
  },
3987
- period: {
3988
- type: 'string',
3989
- description: 'Period requested',
3990
- example: '6m'
3991
- },
3992
- granularity: {
4339
+ createdAt: {
4340
+ format: 'date-time',
3993
4341
  type: 'string',
3994
- description: 'Data granularity (v1 returns month buckets)',
3995
- example: 'month'
4342
+ description: 'Created at timestamp'
3996
4343
  },
3997
- currency: {
4344
+ updatedAt: {
4345
+ format: 'date-time',
3998
4346
  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
- }
4347
+ description: 'Updated at timestamp'
4008
4348
  }
4009
4349
  },
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: {}
4350
+ required: [
4351
+ 'id',
4352
+ 'userId',
4353
+ 'ruleId',
4354
+ 'expectedDate',
4355
+ 'expectedAmount',
4356
+ 'status',
4357
+ 'isOverdue',
4358
+ 'rule',
4359
+ 'createdAt',
4360
+ 'updatedAt'
4361
+ ]
4031
4362
  } as const;
4032
4363
 
4033
- export const $DeleteOwnUserDto = {
4364
+ export const $ExpectedTransactionListResponseDto = {
4034
4365
  type: 'object',
4035
4366
  properties: {
4036
- accessToken: {
4037
- type: 'string',
4038
- description: 'Access token for user verification',
4039
- example: 'abc123xyz'
4367
+ items: {
4368
+ type: 'array',
4369
+ items: {
4370
+ $ref: '#/components/schemas/ExpectedTransactionResponseDto'
4371
+ }
4372
+ },
4373
+ total: {
4374
+ type: 'number',
4375
+ description: 'Total count'
4040
4376
  }
4041
4377
  },
4042
- required: ['accessToken']
4378
+ required: ['items', 'total']
4043
4379
  } as const;
4044
4380
 
4045
- export const $SignupDto = {
4381
+ export const $ConfirmMatchDto = {
4046
4382
  type: 'object',
4047
4383
  properties: {
4048
- turnstileToken: {
4384
+ transactionId: {
4049
4385
  type: 'string',
4050
- description:
4051
- 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4052
- example: '0.abc123def456...'
4386
+ description: 'Transaction ID to match with'
4053
4387
  }
4054
- }
4388
+ },
4389
+ required: ['transactionId']
4055
4390
  } as const;
4056
4391
 
4057
- export const $SignupResponseDto = {
4392
+ export const $EnterNowDto = {
4058
4393
  type: 'object',
4059
4394
  properties: {
4060
- authToken: {
4395
+ expenseAccount: {
4061
4396
  type: 'string',
4062
- description: 'JWT auth token',
4063
- example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
4397
+ description:
4398
+ 'Override expense account (uses rule default if not provided)',
4399
+ maxLength: 200
4064
4400
  },
4065
- accessToken: {
4401
+ paymentAccount: {
4066
4402
  type: 'string',
4067
- description: 'Auto-generated access token'
4403
+ description:
4404
+ 'Override payment account (uses rule default if not provided)',
4405
+ maxLength: 200
4406
+ },
4407
+ amount: {
4408
+ type: 'number',
4409
+ description: 'Override amount (uses expected amount if not provided)',
4410
+ minimum: 0
4411
+ },
4412
+ payee: {
4413
+ type: 'string',
4414
+ description: 'Override payee (uses rule default if not provided)',
4415
+ maxLength: 200
4068
4416
  },
4069
- role: {
4417
+ narration: {
4070
4418
  type: 'string',
4071
- description: 'Assigned user role',
4072
- enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
4419
+ description: 'Optional narration',
4420
+ maxLength: 500
4073
4421
  }
4074
- },
4075
- required: ['authToken', 'accessToken', 'role']
4422
+ }
4076
4423
  } as const;
4077
4424
 
4078
- export const $UpdateUserSettingDto = {
4425
+ export const $ForecastItemDto = {
4079
4426
  type: 'object',
4080
4427
  properties: {
4081
- secId: {
4082
- type: 'number',
4083
- description: 'Security ID'
4428
+ rule: {
4429
+ type: 'string',
4430
+ description: 'Rule name',
4431
+ example: 'Rent'
4084
4432
  },
4085
- annualInterestRate: {
4433
+ ruleId: {
4434
+ type: 'string',
4435
+ description: 'Rule ID',
4436
+ example: 'clx123...'
4437
+ },
4438
+ amount: {
4086
4439
  type: 'number',
4087
- description: 'Annual interest rate',
4088
- example: 0.05
4440
+ description: 'Expected amount',
4441
+ example: 3000
4089
4442
  },
4090
- currency: {
4443
+ date: {
4091
4444
  type: 'string',
4092
- description: 'Currency code',
4093
- example: 'USD'
4445
+ description: 'Expected date (YYYY-MM-DD)',
4446
+ example: '2024-04-01'
4094
4447
  },
4095
- baseCurrency: {
4448
+ icon: {
4096
4449
  type: 'string',
4097
- description: 'Base currency code',
4098
- example: 'USD'
4450
+ description: 'Rule icon emoji',
4451
+ example: '🏠',
4452
+ nullable: true
4099
4453
  },
4100
- benchmark: {
4454
+ currency: {
4101
4455
  type: 'string',
4102
- description: 'Benchmark symbol',
4103
- example: 'SPY'
4104
- },
4105
- colorScheme: {
4456
+ description: 'Currency code',
4457
+ example: 'CNY'
4458
+ }
4459
+ },
4460
+ required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
4461
+ } as const;
4462
+
4463
+ export const $MonthlyForecastDto = {
4464
+ type: 'object',
4465
+ properties: {
4466
+ month: {
4106
4467
  type: 'string',
4107
- description: 'Color scheme',
4108
- enum: ['DARK', 'LIGHT']
4468
+ description: 'Month (YYYY-MM)',
4469
+ example: '2024-04'
4109
4470
  },
4110
- dateRange: {
4111
- type: 'string',
4112
- description: 'Date range filter',
4113
- example: '1y'
4471
+ expectedOutflow: {
4472
+ type: 'number',
4473
+ description: 'Total expected outflow for the month',
4474
+ example: 8500
4114
4475
  },
4115
- emergencyFund: {
4476
+ itemCount: {
4116
4477
  type: 'number',
4117
- description: 'Emergency fund amount',
4118
- example: 10000
4478
+ description: 'Number of expected transactions',
4479
+ example: 3
4119
4480
  },
4120
- 'filters.accounts': {
4121
- description: 'Account filter IDs',
4122
- type: 'array',
4123
- items: {
4124
- type: 'string'
4481
+ byCurrency: {
4482
+ type: 'object',
4483
+ description: 'Breakdown by currency',
4484
+ example: {
4485
+ CNY: 8500,
4486
+ USD: 100
4125
4487
  }
4126
4488
  },
4127
- 'filters.assetClasses': {
4128
- description: 'Asset class filters',
4489
+ items: {
4490
+ description: 'Individual forecast items',
4129
4491
  type: 'array',
4130
4492
  items: {
4131
- type: 'string'
4493
+ $ref: '#/components/schemas/ForecastItemDto'
4132
4494
  }
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',
4495
+ }
4496
+ },
4497
+ required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
4498
+ } as const;
4499
+
4500
+ export const $ForecastResponseDto = {
4501
+ type: 'object',
4502
+ properties: {
4503
+ forecast: {
4504
+ description: 'Monthly forecast data',
4144
4505
  type: 'array',
4145
4506
  items: {
4146
- type: 'string'
4507
+ $ref: '#/components/schemas/MonthlyForecastDto'
4147
4508
  }
4148
4509
  },
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: {
4510
+ totalOutflow: {
4168
4511
  type: 'number',
4169
- description: 'Projected total amount',
4170
- example: 1000000
4512
+ description: 'Total expected outflow across all months',
4513
+ example: 25500
4171
4514
  },
4172
- retirementDate: {
4173
- type: 'string',
4174
- description: 'Retirement date in ISO 8601 format',
4175
- example: '2050-01-01'
4515
+ totalByCurrency: {
4516
+ type: 'object',
4517
+ description: 'Total by currency across all months',
4518
+ example: {
4519
+ CNY: 25500,
4520
+ USD: 300
4521
+ }
4176
4522
  },
4177
- savingsRate: {
4523
+ rulesCount: {
4178
4524
  type: 'number',
4179
- description: 'Savings rate percentage',
4180
- example: 0.2
4525
+ description: 'Number of active recurring rules included',
4526
+ example: 5
4181
4527
  },
4182
- viewMode: {
4528
+ periodStart: {
4183
4529
  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: {
4530
+ description: 'Forecast period start date',
4531
+ example: '2024-04-01'
4532
+ },
4533
+ periodEnd: {
4194
4534
  type: 'string',
4195
- description: 'Property value'
4535
+ description: 'Forecast period end date',
4536
+ example: '2024-06-30'
4196
4537
  }
4197
4538
  },
4198
- required: ['value']
4539
+ required: [
4540
+ 'forecast',
4541
+ 'totalOutflow',
4542
+ 'totalByCurrency',
4543
+ 'rulesCount',
4544
+ 'periodStart',
4545
+ 'periodEnd'
4546
+ ]
4199
4547
  } as const;
4200
4548
 
4201
4549
  export const $CreateTransactionRuleDto = {
@@ -4757,7 +5105,8 @@ export const $UpdateTransactionRuleDto = {
4757
5105
  },
4758
5106
  matchLogic: {
4759
5107
  type: 'string',
4760
- enum: ['OR', 'AND']
5108
+ enum: ['OR', 'AND'],
5109
+ default: 'OR'
4761
5110
  },
4762
5111
  amountMin: {
4763
5112
  type: 'number',
@@ -4771,13 +5120,10 @@ export const $UpdateTransactionRuleDto = {
4771
5120
  },
4772
5121
  priority: {
4773
5122
  type: 'number',
5123
+ default: 50,
4774
5124
  minimum: 0,
4775
5125
  maximum: 1000
4776
5126
  },
4777
- enabled: {
4778
- type: 'boolean',
4779
- description: 'Enable or disable the rule'
4780
- },
4781
5127
  additionalTags: {
4782
5128
  items: {
4783
5129
  type: 'array'
@@ -4787,6 +5133,10 @@ export const $UpdateTransactionRuleDto = {
4787
5133
  },
4788
5134
  additionalMetadata: {
4789
5135
  type: 'object'
5136
+ },
5137
+ enabled: {
5138
+ type: 'boolean',
5139
+ description: 'Enable or disable the rule'
4790
5140
  }
4791
5141
  }
4792
5142
  } as const;
@@ -4815,36 +5165,113 @@ export const $TestRuleDto = {
4815
5165
  maxLength: 10
4816
5166
  }
4817
5167
  },
4818
- required: ['narration']
5168
+ required: ['narration']
5169
+ } as const;
5170
+
5171
+ export const $TestRuleResponseDto = {
5172
+ type: 'object',
5173
+ properties: {
5174
+ ruleId: {
5175
+ type: 'string',
5176
+ description: 'Rule ID that was tested'
5177
+ },
5178
+ matches: {
5179
+ type: 'boolean',
5180
+ description: 'Whether the rule matched the test data'
5181
+ },
5182
+ confidence: {
5183
+ type: 'number',
5184
+ description: 'Match confidence score (0-1)',
5185
+ example: 0.85
5186
+ },
5187
+ matchDetails: {
5188
+ type: 'object',
5189
+ description: 'Details of which fields matched',
5190
+ example: {
5191
+ narration: true,
5192
+ payee: false,
5193
+ categoryAccount: false
5194
+ }
5195
+ }
5196
+ },
5197
+ required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5198
+ } as const;
5199
+
5200
+ export const $CategoryCatalogEntryDto = {
5201
+ type: 'object',
5202
+ properties: {
5203
+ slug: {
5204
+ type: 'string',
5205
+ description: 'Category slug (single source-of-truth)',
5206
+ example: 'food'
5207
+ },
5208
+ scenario: {
5209
+ type: 'string',
5210
+ description: 'Display scenario group (maps to frontend picker _scenario)',
5211
+ enum: [
5212
+ 'expense',
5213
+ 'income',
5214
+ 'investment',
5215
+ 'banking',
5216
+ 'transfer',
5217
+ 'payment'
5218
+ ],
5219
+ example: 'expense'
5220
+ },
5221
+ icon: {
5222
+ type: 'string',
5223
+ description: 'Lucide icon name',
5224
+ example: 'utensils'
5225
+ },
5226
+ regions: {
5227
+ description: "Applicable regions ('*' = all, 'cn' = CN-only)",
5228
+ example: ['*'],
5229
+ type: 'array',
5230
+ items: {
5231
+ type: 'string'
5232
+ }
5233
+ },
5234
+ categoryAccounts: {
5235
+ description:
5236
+ "Beancount account paths (categoryAccount) of the region-enabled system rules whose categoryKeywords include this slug (#816). System rules only (public endpoint — user rules excluded); one-to-many by design (e.g. 'utilities' → Electricity/Water/Internet/Gas), sorted, [] when no rule maps the slug.",
5237
+ example: [
5238
+ 'Expenses:Utilities:Electricity',
5239
+ 'Expenses:Utilities:Gas',
5240
+ 'Expenses:Utilities:Internet',
5241
+ 'Expenses:Utilities:Water'
5242
+ ],
5243
+ type: 'array',
5244
+ items: {
5245
+ type: 'string'
5246
+ }
5247
+ }
5248
+ },
5249
+ required: ['slug', 'scenario', 'icon', 'regions', 'categoryAccounts']
4819
5250
  } as const;
4820
5251
 
4821
- export const $TestRuleResponseDto = {
5252
+ export const $CategoryCatalogListResponseDto = {
4822
5253
  type: 'object',
4823
5254
  properties: {
4824
- ruleId: {
4825
- type: 'string',
4826
- description: 'Rule ID that was tested'
4827
- },
4828
- matches: {
4829
- type: 'boolean',
4830
- description: 'Whether the rule matched the test data'
5255
+ items: {
5256
+ description: 'Category entries (region-scoped, query-filtered)',
5257
+ type: 'array',
5258
+ items: {
5259
+ $ref: '#/components/schemas/CategoryCatalogEntryDto'
5260
+ }
4831
5261
  },
4832
- confidence: {
5262
+ total: {
4833
5263
  type: 'number',
4834
- description: 'Match confidence score (0-1)',
4835
- example: 0.85
5264
+ description:
5265
+ 'Total category entries for the region (before query filtering)',
5266
+ example: 30
4836
5267
  },
4837
- matchDetails: {
4838
- type: 'object',
4839
- description: 'Details of which fields matched',
4840
- example: {
4841
- narration: true,
4842
- payee: false,
4843
- categoryAccount: false
4844
- }
5268
+ region: {
5269
+ type: 'string',
5270
+ description: 'Region code',
5271
+ example: 'cn'
4845
5272
  }
4846
5273
  },
4847
- required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5274
+ required: ['items', 'total', 'region']
4848
5275
  } as const;
4849
5276
 
4850
5277
  export const $CreateBeanEventDto = {
@@ -5010,6 +5437,14 @@ export const $OnboardingAccountDto = {
5010
5437
  description:
5011
5438
  'Platform ID to bind the account to (references Platform.id); omit for unbound',
5012
5439
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
5440
+ },
5441
+ displayName: {
5442
+ type: 'string',
5443
+ description:
5444
+ 'User-set display name override (omit/null = keep the derived name)',
5445
+ nullable: true,
5446
+ maxLength: 50,
5447
+ example: 'Salary card'
5013
5448
  }
5014
5449
  },
5015
5450
  required: ['path', 'currency']
@@ -5589,7 +6024,8 @@ export const $UpdateMapperDefaultsDto = {
5589
6024
  type: 'string',
5590
6025
  description: 'Source account for transactions (Beancount format)',
5591
6026
  example: 'Assets:CN:Alipay:Balance',
5592
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6027
+ pattern:
6028
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5593
6029
  },
5594
6030
  currency: {
5595
6031
  type: 'string',
@@ -5603,13 +6039,15 @@ export const $UpdateMapperDefaultsDto = {
5603
6039
  type: 'string',
5604
6040
  description: 'Default expense account (optional)',
5605
6041
  example: 'Expenses:Unknown',
5606
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6042
+ pattern:
6043
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5607
6044
  },
5608
6045
  incomeAccount: {
5609
6046
  type: 'string',
5610
6047
  description: 'Default income account (optional)',
5611
6048
  example: 'Income:Unknown',
5612
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6049
+ pattern:
6050
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5613
6051
  },
5614
6052
  methodAccountMapping: {
5615
6053
  type: 'object',
@@ -5666,12 +6104,14 @@ export const $ProviderSyncConfigDto = {
5666
6104
  },
5667
6105
  defaultExpenseAccount: {
5668
6106
  type: 'string',
5669
- description: 'Default expense account for the second posting',
6107
+ description:
6108
+ 'Default expense account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5670
6109
  example: 'Expenses:Unknown'
5671
6110
  },
5672
6111
  defaultIncomeAccount: {
5673
6112
  type: 'string',
5674
- description: 'Default income account for the second posting',
6113
+ description:
6114
+ 'Default income account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5675
6115
  example: 'Income:Unknown'
5676
6116
  },
5677
6117
  filterPending: {
@@ -5686,12 +6126,7 @@ export const $ProviderSyncConfigDto = {
5686
6126
  example: 'acc_gocardless_001'
5687
6127
  }
5688
6128
  },
5689
- required: [
5690
- 'sourceAccount',
5691
- 'defaultCurrency',
5692
- 'defaultExpenseAccount',
5693
- 'defaultIncomeAccount'
5694
- ]
6129
+ required: ['sourceAccount', 'defaultCurrency']
5695
6130
  } as const;
5696
6131
 
5697
6132
  export const $ProviderSyncDto = {
@@ -5901,15 +6336,106 @@ export const $UncoveredFormatMissDto = {
5901
6336
  properties: {}
5902
6337
  } as const;
5903
6338
 
6339
+ export const $ClientParsedDataDto = {
6340
+ type: 'object',
6341
+ properties: {
6342
+ amount: {
6343
+ type: 'number',
6344
+ description: 'Transaction amount',
6345
+ example: 35
6346
+ },
6347
+ currency: {
6348
+ type: 'string',
6349
+ description: 'Currency code',
6350
+ example: 'CNY'
6351
+ },
6352
+ date: {
6353
+ type: 'string',
6354
+ description: 'Transaction date (ISO 8601)',
6355
+ example: '2026-08-15'
6356
+ },
6357
+ payee: {
6358
+ type: 'string',
6359
+ description: 'Payee/merchant name',
6360
+ example: 'Starbucks'
6361
+ },
6362
+ narration: {
6363
+ type: 'string',
6364
+ description: 'Transaction narration'
6365
+ },
6366
+ category: {
6367
+ type: 'string',
6368
+ description:
6369
+ 'Category in canonical form (catalog slug or Stage-2 rule keyword token, ADR-0116) — echo back verbatim from parsedData.category. Foreign forms (locale display names, account paths) are rejected with nlp.category.invalid.',
6370
+ example: 'food'
6371
+ },
6372
+ incomeType: {
6373
+ type: 'string',
6374
+ description: 'Income type',
6375
+ example: 'Salary'
6376
+ },
6377
+ incomeSource: {
6378
+ type: 'string',
6379
+ description: 'Income source',
6380
+ example: 'Anthropic Inc.'
6381
+ },
6382
+ symbol: {
6383
+ type: 'string',
6384
+ description: 'Security symbol code (e.g., 600519, AAPL)',
6385
+ example: 'AAPL'
6386
+ },
6387
+ quantity: {
6388
+ type: 'number',
6389
+ description: 'Quantity of shares/units',
6390
+ example: 100
6391
+ },
6392
+ price: {
6393
+ type: 'number',
6394
+ description: 'Unit price per share/unit',
6395
+ example: 1900
6396
+ },
6397
+ investmentAction: {
6398
+ type: 'string',
6399
+ description: 'Investment action',
6400
+ enum: ['buy', 'sell'],
6401
+ example: 'buy'
6402
+ },
6403
+ paymentSource: {
6404
+ type: 'string',
6405
+ description: 'Payment source: asset (default) or liability (credit card)',
6406
+ enum: ['asset', 'liability'],
6407
+ example: 'asset'
6408
+ },
6409
+ liabilityHint: {
6410
+ type: 'string',
6411
+ description: 'Liability account hint (CreditCard/Huabei/Baitiao)',
6412
+ example: 'CreditCard'
6413
+ },
6414
+ warning: {
6415
+ type: 'string',
6416
+ description:
6417
+ 'Display-only warning from the prior response; accepted but ignored.',
6418
+ example: 'Cross-currency settlement applies.'
6419
+ }
6420
+ }
6421
+ } as const;
6422
+
5904
6423
  export const $ProcessNlpDto = {
5905
6424
  type: 'object',
5906
6425
  properties: {
5907
6426
  message: {
5908
6427
  type: 'string',
5909
- description: 'Natural language text describing a transaction (Chinese)',
5910
- example: 'yesterday Starbucks spent 35 yuan',
6428
+ description:
6429
+ 'Natural language text describing a transaction. Optional when `confirm` is true (structured confirm); otherwise required.',
6430
+ example: 'Starbucks 35',
5911
6431
  maxLength: 500
5912
6432
  },
6433
+ confirm: {
6434
+ type: 'boolean',
6435
+ description:
6436
+ 'Structured confirm signal — bypasses NL confirm-word matching when true. Send parsedData field edits alongside. The NL word-list path is the fallback.',
6437
+ example: true
6438
+ },
5913
6439
  sessionId: {
5914
6440
  type: 'string',
5915
6441
  description:
@@ -5917,17 +6443,51 @@ export const $ProcessNlpDto = {
5917
6443
  example: 'session_abc123'
5918
6444
  },
5919
6445
  parsedData: {
5920
- type: 'object',
5921
6446
  description:
5922
6447
  'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
5923
6448
  example: {
5924
6449
  amount: 35,
5925
6450
  currency: 'CNY',
5926
6451
  payee: 'Starbucks'
5927
- }
6452
+ },
6453
+ allOf: [
6454
+ {
6455
+ $ref: '#/components/schemas/ClientParsedDataDto'
6456
+ }
6457
+ ]
6458
+ },
6459
+ selectedRuleId: {
6460
+ type: 'string',
6461
+ description:
6462
+ '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.',
6463
+ example: 'rule_abc123'
6464
+ },
6465
+ selectedAccount: {
6466
+ type: 'string',
6467
+ description:
6468
+ '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.',
6469
+ example: 'Expenses:Food:Coffee'
6470
+ },
6471
+ viewpointAccount: {
6472
+ type: 'string',
6473
+ description:
6474
+ '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.',
6475
+ example: 'Assets:CN:Bank:ICBC'
6476
+ },
6477
+ viewpointCategory: {
6478
+ type: 'string',
6479
+ description:
6480
+ "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.",
6481
+ example: 'Food'
6482
+ },
6483
+ viewpointFlow: {
6484
+ type: 'string',
6485
+ description:
6486
+ "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.",
6487
+ enum: ['income', 'expense'],
6488
+ example: 'expense'
5928
6489
  }
5929
- },
5930
- required: ['message']
6490
+ }
5931
6491
  } as const;
5932
6492
 
5933
6493
  export const $NlpTransactionInfoDto = {
@@ -5993,7 +6553,9 @@ export const $NlpParsedDataDto = {
5993
6553
  },
5994
6554
  category: {
5995
6555
  type: 'string',
5996
- description: 'Category'
6556
+ description:
6557
+ 'Category in canonical form: a CATEGORY_CATALOG slug (GET /{region}/bean/categories) or a Stage-2 rule categoryKeywords token (ADR-0116 D2). Never a locale display name or a beancount account path. Echo back verbatim on confirm.',
6558
+ example: 'food'
5997
6559
  },
5998
6560
  incomeType: {
5999
6561
  type: 'string',
@@ -6239,6 +6801,24 @@ export const $NlpRuleConfirmationDataDto = {
6239
6801
  ]
6240
6802
  } as const;
6241
6803
 
6804
+ export const $NlpAccountCandidateDto = {
6805
+ type: 'object',
6806
+ properties: {
6807
+ path: {
6808
+ type: 'string',
6809
+ description: 'Canonical beancount account path (echo back on selection)',
6810
+ example: 'Expenses:Food:Dining'
6811
+ },
6812
+ name: {
6813
+ type: 'string',
6814
+ description:
6815
+ 'Localized display name (ADR-0114 read-time projection, user locale)',
6816
+ example: '餐饮'
6817
+ }
6818
+ },
6819
+ required: ['path', 'name']
6820
+ } as const;
6821
+
6242
6822
  export const $NlpAccountConfirmationDataDto = {
6243
6823
  type: 'object',
6244
6824
  properties: {
@@ -6249,14 +6829,16 @@ export const $NlpAccountConfirmationDataDto = {
6249
6829
  },
6250
6830
  suggestedAccount: {
6251
6831
  type: 'string',
6252
- description: 'Suggested replacement account',
6832
+ description:
6833
+ 'Suggested replacement account (omitted when no clear candidate)',
6253
6834
  example: 'Expenses:Food:Drinks'
6254
6835
  },
6255
6836
  similarAccounts: {
6256
- description: 'Similar accounts for user selection',
6837
+ description:
6838
+ 'Similar accounts for user selection (path + localized name, #680)',
6257
6839
  type: 'array',
6258
6840
  items: {
6259
- type: 'string'
6841
+ $ref: '#/components/schemas/NlpAccountCandidateDto'
6260
6842
  }
6261
6843
  },
6262
6844
  errorMessage: {
@@ -6271,7 +6853,6 @@ export const $NlpAccountConfirmationDataDto = {
6271
6853
  },
6272
6854
  required: [
6273
6855
  'invalidAccount',
6274
- 'suggestedAccount',
6275
6856
  'similarAccounts',
6276
6857
  'errorMessage',
6277
6858
  'transactionContext'
@@ -6444,7 +7025,8 @@ export const $NlpSuggestedAccountDto = {
6444
7025
  },
6445
7026
  confidence: {
6446
7027
  type: 'number',
6447
- description: 'Confidence score for this suggestion (0-1)',
7028
+ description:
7029
+ 'Confidence score for this suggestion (0-1). Present = predicted (confirm/confirm_rule/confirm_account); omitted = actual persisted account (created). (#586)',
6448
7030
  example: 0.9
6449
7031
  }
6450
7032
  },
@@ -6480,23 +7062,31 @@ export const $NlpDefaultAccountsDto = {
6480
7062
  properties: {
6481
7063
  asset: {
6482
7064
  type: 'string',
6483
- description: 'Default asset account',
6484
- example: 'Assets:Checking'
7065
+ description:
7066
+ 'Default OPEN asset account (MRU when multiple), or null when none/ambiguous',
7067
+ example: 'Assets:Checking',
7068
+ nullable: true
6485
7069
  },
6486
7070
  expense: {
6487
7071
  type: 'string',
6488
- description: 'Default expense account',
6489
- example: 'Expenses:Uncategorized'
7072
+ description:
7073
+ 'Default OPEN expense account (MRU when multiple), or null when none/ambiguous',
7074
+ example: 'Expenses:Food:Coffee',
7075
+ nullable: true
6490
7076
  },
6491
7077
  income: {
6492
7078
  type: 'string',
6493
- description: 'Default income account',
6494
- example: 'Income:Uncategorized'
7079
+ description:
7080
+ 'Default OPEN income account (MRU when multiple), or null when none/ambiguous',
7081
+ example: 'Income:Salary',
7082
+ nullable: true
6495
7083
  },
6496
7084
  liability: {
6497
7085
  type: 'string',
6498
- description: 'Default liability account',
6499
- example: 'Liabilities:CreditCard'
7086
+ description:
7087
+ 'Default OPEN liability account (MRU when multiple), or null when none/ambiguous',
7088
+ example: 'Liabilities:CreditCard',
7089
+ nullable: true
6500
7090
  }
6501
7091
  },
6502
7092
  required: ['asset', 'expense', 'income', 'liability']
@@ -6536,7 +7126,7 @@ export const $NlpResponseDto = {
6536
7126
  type: 'string',
6537
7127
  description:
6538
7128
  'Asset sub-type (only present when intent is "asset"). Determines which asset-related form to render.',
6539
- enum: ['transfer', 'banking', 'investment'],
7129
+ enum: ['transfer', 'banking', 'investment', 'lend', 'lend_collect'],
6540
7130
  example: 'investment'
6541
7131
  },
6542
7132
  liabilitySubType: {
@@ -6684,7 +7274,7 @@ export const $NlpResponseDto = {
6684
7274
  },
6685
7275
  suggestedAccounts: {
6686
7276
  description:
6687
- 'Suggested accounts for this transaction. Contains recommended source and destination accounts based on the detected intent and rules.',
7277
+ '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
7278
  allOf: [
6689
7279
  {
6690
7280
  $ref: '#/components/schemas/NlpSuggestedAccountsDto'
@@ -6693,7 +7283,7 @@ export const $NlpResponseDto = {
6693
7283
  },
6694
7284
  defaultAccounts: {
6695
7285
  description:
6696
- 'Default accounts for the user/region. These are fallback accounts used when no specific suggestion is available.',
7286
+ 'Default fallback accounts for the user/region (#586). v1 returns universal constants; per-user personalization is planned.',
6697
7287
  allOf: [
6698
7288
  {
6699
7289
  $ref: '#/components/schemas/NlpDefaultAccountsDto'
@@ -6739,13 +7329,26 @@ export const $PlatformListItemDto = {
6739
7329
  suggestedSegment: {
6740
7330
  type: 'string',
6741
7331
  description:
6742
- 'Suggested path segment — canonical with first char uppercased (ACC_COMP_NAME_RE)'
7332
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6743
7333
  },
6744
7334
  logoUrl: {
6745
7335
  type: 'string',
6746
7336
  description: 'Logo URL',
6747
7337
  nullable: true
6748
7338
  },
7339
+ countryCode: {
7340
+ type: 'string',
7341
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7342
+ example: 'CN',
7343
+ nullable: true
7344
+ },
7345
+ category: {
7346
+ type: 'string',
7347
+ description:
7348
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7349
+ nullable: true,
7350
+ example: 'DigitalWallet'
7351
+ },
6749
7352
  isBound: {
6750
7353
  type: 'boolean',
6751
7354
  description: 'Whether user has accounts using this platform'
@@ -6759,6 +7362,8 @@ export const $PlatformListItemDto = {
6759
7362
  'canonical',
6760
7363
  'suggestedSegment',
6761
7364
  'logoUrl',
7365
+ 'countryCode',
7366
+ 'category',
6762
7367
  'isBound'
6763
7368
  ]
6764
7369
  } as const;
@@ -6794,13 +7399,26 @@ export const $PlatformMatchResultDto = {
6794
7399
  suggestedSegment: {
6795
7400
  type: 'string',
6796
7401
  description:
6797
- 'Suggested path segment — canonical, already in ACCOUNT_RE format'
7402
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6798
7403
  },
6799
7404
  logoUrl: {
6800
7405
  type: 'string',
6801
7406
  description: 'Logo URL',
6802
7407
  nullable: true
6803
7408
  },
7409
+ countryCode: {
7410
+ type: 'string',
7411
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7412
+ example: 'CN',
7413
+ nullable: true
7414
+ },
7415
+ category: {
7416
+ type: 'string',
7417
+ description:
7418
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7419
+ nullable: true,
7420
+ example: 'DigitalWallet'
7421
+ },
6804
7422
  matchType: {
6805
7423
  type: 'string',
6806
7424
  description: "How this row matched: 'exact' > 'prefix' > 'substring'",
@@ -6814,6 +7432,8 @@ export const $PlatformMatchResultDto = {
6814
7432
  'type',
6815
7433
  'suggestedSegment',
6816
7434
  'logoUrl',
7435
+ 'countryCode',
7436
+ 'category',
6817
7437
  'matchType'
6818
7438
  ]
6819
7439
  } as const;
@@ -6843,7 +7463,80 @@ export const $PlatformMatchResponseDto = {
6843
7463
  description: 'true when total > platforms.length (more matches exist)'
6844
7464
  }
6845
7465
  },
6846
- required: ['platforms', 'matchType', 'total', 'hasMore']
7466
+ required: ['platforms', 'matchType', 'total', 'hasMore']
7467
+ } as const;
7468
+
7469
+ export const $PlatformStandardsPlatformDto = {
7470
+ type: 'object',
7471
+ properties: {
7472
+ id: {
7473
+ type: 'string',
7474
+ description: 'Global platform ID'
7475
+ },
7476
+ name: {
7477
+ type: 'string',
7478
+ description: 'Platform name (e.g., "ICBC")'
7479
+ },
7480
+ canonical: {
7481
+ type: 'string',
7482
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7483
+ },
7484
+ suggestedSegment: {
7485
+ type: 'string',
7486
+ description:
7487
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7488
+ },
7489
+ type: {
7490
+ type: 'string',
7491
+ description: 'Platform type',
7492
+ enum: [
7493
+ 'BANK',
7494
+ 'BROKERAGE',
7495
+ 'CRYPTO_EXCHANGE',
7496
+ 'PAYMENT',
7497
+ 'INVESTMENT',
7498
+ 'INSURANCE',
7499
+ 'OTHER'
7500
+ ]
7501
+ },
7502
+ category: {
7503
+ type: 'string',
7504
+ description:
7505
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank) resolved against the final region. null = no region-aware suggestion; fall back to type.',
7506
+ nullable: true,
7507
+ example: 'Bank'
7508
+ }
7509
+ },
7510
+ required: ['id', 'name', 'canonical', 'suggestedSegment', 'type', 'category']
7511
+ } as const;
7512
+
7513
+ export const $PlatformStandardsResponseDto = {
7514
+ type: 'object',
7515
+ properties: {
7516
+ platform: {
7517
+ description: 'The selected platform (institution lock source)',
7518
+ allOf: [
7519
+ {
7520
+ $ref: '#/components/schemas/PlatformStandardsPlatformDto'
7521
+ }
7522
+ ]
7523
+ },
7524
+ region: {
7525
+ type: 'string',
7526
+ description:
7527
+ "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.",
7528
+ example: 'CN'
7529
+ },
7530
+ templates: {
7531
+ description:
7532
+ 'Candidate account-standard templates of the resolved region (groupable by productCategory client-side)',
7533
+ type: 'array',
7534
+ items: {
7535
+ $ref: '#/components/schemas/AccountStandardResponseDto'
7536
+ }
7537
+ }
7538
+ },
7539
+ required: ['platform', 'region', 'templates']
6847
7540
  } as const;
6848
7541
 
6849
7542
  export const $CreatePlatformDto = {
@@ -6947,7 +7640,8 @@ export const $UpdatePlatformDto = {
6947
7640
  },
6948
7641
  isActive: {
6949
7642
  type: 'boolean',
6950
- description: 'Whether the platform is active'
7643
+ description: 'Whether the platform is active',
7644
+ default: true
6951
7645
  }
6952
7646
  }
6953
7647
  } as const;
@@ -7110,7 +7804,8 @@ export const $AccountItemDto = {
7110
7804
  },
7111
7805
  displayName: {
7112
7806
  type: 'string',
7113
- description: 'Display name (last part of account path)',
7807
+ description:
7808
+ 'Display name: user-set name if provided (#762), else the ADR-0114 chain — request-locale catalog name, en pivot, then the last part of the account path (#771)',
7114
7809
  example: 'Savings'
7115
7810
  },
7116
7811
  balance: {
@@ -7146,7 +7841,8 @@ export const $PlatformGroupDto = {
7146
7841
  example: 'CMB Bank'
7147
7842
  },
7148
7843
  accounts: {
7149
- description: 'Accounts within this platform',
7844
+ description:
7845
+ 'Accounts within this platform (Assets and Liabilities rows, #696)',
7150
7846
  type: 'array',
7151
7847
  items: {
7152
7848
  $ref: '#/components/schemas/AccountItemDto'
@@ -7154,7 +7850,8 @@ export const $PlatformGroupDto = {
7154
7850
  },
7155
7851
  totalBalance: {
7156
7852
  type: 'string',
7157
- description: 'FX-converted total balance in base currency',
7853
+ description:
7854
+ 'FX-converted total balance in base currency (nets Assets + Liabilities rows; can be negative)',
7158
7855
  example: '100000.00'
7159
7856
  },
7160
7857
  balanceByCurrency: {
@@ -7173,7 +7870,7 @@ export const $PlatformGroupDto = {
7173
7870
  sharePct: {
7174
7871
  type: 'number',
7175
7872
  description:
7176
- 'Share of the grand converted total (0-100); 0 when grand total is 0',
7873
+ '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
7874
  example: 42.5
7178
7875
  }
7179
7876
  },
@@ -7221,7 +7918,8 @@ export const $AccountsSummaryDto = {
7221
7918
  properties: {
7222
7919
  totalAccounts: {
7223
7920
  type: 'number',
7224
- description: 'Total number of accounts'
7921
+ description:
7922
+ 'Total number of accounts (balance sheet: Assets + Liabilities, #696)'
7225
7923
  },
7226
7924
  totalPlatforms: {
7227
7925
  type: 'number',
@@ -7279,7 +7977,8 @@ export const $AccountItemWithAssetClassDto = {
7279
7977
  },
7280
7978
  displayName: {
7281
7979
  type: 'string',
7282
- description: 'Display name (last part of account path)',
7980
+ description:
7981
+ 'Display name: user-set name if provided (#762), else the ADR-0114 chain — request-locale catalog name, en pivot, then the last part of the account path (#771)',
7283
7982
  example: 'Savings'
7284
7983
  },
7285
7984
  balance: {
@@ -7765,7 +8464,7 @@ export const $MonetaryDto = {
7765
8464
  example: 'USD'
7766
8465
  },
7767
8466
  baseCcyEquivalent: {
7768
- type: 'object',
8467
+ type: 'string',
7769
8468
  description: 'Converted to user base currency (Decimal string)',
7770
8469
  example: '21600',
7771
8470
  nullable: true
@@ -7841,13 +8540,13 @@ export const $HoldingPnlRowDto = {
7841
8540
  example: 'Assets:US:Broker:AAPL'
7842
8541
  },
7843
8542
  accountCcy: {
7844
- type: 'object',
8543
+ type: 'string',
7845
8544
  description: 'Account settlement currency (ISO 4217), from cost currency',
7846
8545
  nullable: true,
7847
8546
  example: 'USD'
7848
8547
  },
7849
8548
  brokerType: {
7850
- type: 'object',
8549
+ type: 'string',
7851
8550
  description: 'Broker type derived from Platform.type',
7852
8551
  nullable: true,
7853
8552
  example: 'broker'
@@ -7868,7 +8567,7 @@ export const $HoldingPnlRowDto = {
7868
8567
  example: 'EQUITY'
7869
8568
  },
7870
8569
  assetSubClass: {
7871
- type: 'object',
8570
+ type: 'string',
7872
8571
  nullable: true,
7873
8572
  example: 'STOCK'
7874
8573
  },
@@ -7915,14 +8614,14 @@ export const $HoldingPnlRowDto = {
7915
8614
  ]
7916
8615
  },
7917
8616
  unrealizedPnlBase: {
7918
- type: 'object',
8617
+ type: 'string',
7919
8618
  description:
7920
8619
  'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
7921
8620
  nullable: true,
7922
8621
  example: '6000'
7923
8622
  },
7924
8623
  unrealizedPnlPct: {
7925
- type: 'object',
8624
+ type: 'string',
7926
8625
  description: 'Unrealized P&L % (Decimal string)',
7927
8626
  nullable: true,
7928
8627
  example: '25'
@@ -7946,7 +8645,7 @@ export const $HoldingPnlRowDto = {
7946
8645
  ]
7947
8646
  },
7948
8647
  pctOfInvestedAssets: {
7949
- type: 'object',
8648
+ type: 'string',
7950
8649
  description:
7951
8650
  'Share of invested assets % (Decimal string); only for invested chartTokens',
7952
8651
  nullable: true,
@@ -7991,15 +8690,15 @@ export const $HoldingPnlWarningDto = {
7991
8690
  ]
7992
8691
  },
7993
8692
  symbol: {
7994
- type: 'object',
8693
+ type: 'string',
7995
8694
  nullable: true
7996
8695
  },
7997
8696
  accountId: {
7998
- type: 'object',
8697
+ type: 'string',
7999
8698
  nullable: true
8000
8699
  },
8001
8700
  currency: {
8002
- type: 'object',
8701
+ type: 'string',
8003
8702
  nullable: true
8004
8703
  }
8005
8704
  },
@@ -8062,3 +8761,356 @@ export const $AnonymousLoginResponseDto = {
8062
8761
  },
8063
8762
  required: ['authToken']
8064
8763
  } as const;
8764
+
8765
+ export const $ParserContributionMetaDto = {
8766
+ type: 'object',
8767
+ properties: {
8768
+ institution: {
8769
+ type: 'string',
8770
+ description: 'Institution slug (lowercase kebab-case)',
8771
+ pattern: '^[a-z0-9]+(-[a-z0-9]+)*$',
8772
+ example: 'icbc'
8773
+ },
8774
+ region: {
8775
+ type: 'string',
8776
+ enum: [
8777
+ 'cn',
8778
+ 'us',
8779
+ 'de',
8780
+ 'fr',
8781
+ 'gb',
8782
+ 'hk',
8783
+ 'jp',
8784
+ 'sg',
8785
+ 'au',
8786
+ 'ca',
8787
+ 'other'
8788
+ ]
8789
+ },
8790
+ accountType: {
8791
+ type: 'string',
8792
+ enum: ['checking', 'savings', 'credit', 'debit', 'investment']
8793
+ },
8794
+ format: {
8795
+ type: 'string',
8796
+ enum: ['csv', 'xlsx', 'pdf', 'ofx', 'qif']
8797
+ },
8798
+ institutionDisplayName: {
8799
+ type: 'string',
8800
+ example: '中国工商银行'
8801
+ },
8802
+ encoding: {
8803
+ type: 'string',
8804
+ example: 'utf-8'
8805
+ },
8806
+ delimiter: {
8807
+ type: 'string',
8808
+ description: 'CSV delimiter character: ",", ";", "\\t" or "|"'
8809
+ },
8810
+ headerRows: {
8811
+ type: 'number',
8812
+ default: 1,
8813
+ description: 'Header row count; the client omits the field when it is 1'
8814
+ },
8815
+ notes: {
8816
+ type: 'string',
8817
+ maxLength: 2000
8818
+ }
8819
+ },
8820
+ required: ['institution', 'region', 'accountType', 'format']
8821
+ } as const;
8822
+
8823
+ export const $ParserContributionSamplesDto = {
8824
+ type: 'object',
8825
+ properties: {
8826
+ rows: {
8827
+ description:
8828
+ 'Client-sanitized sample rows (key = column name, value = cell)',
8829
+ type: 'array',
8830
+ items: {
8831
+ type: 'object'
8832
+ }
8833
+ },
8834
+ rawHeaders: {
8835
+ type: 'array',
8836
+ items: {
8837
+ type: 'string'
8838
+ }
8839
+ }
8840
+ },
8841
+ required: ['rows']
8842
+ } as const;
8843
+
8844
+ export const $FieldHintDto = {
8845
+ type: 'object',
8846
+ properties: {
8847
+ columnName: {
8848
+ type: 'string',
8849
+ example: '交易日期'
8850
+ },
8851
+ format: {
8852
+ type: 'string',
8853
+ description: 'Date format, e.g. yyyy-MM-dd HH:mm',
8854
+ example: 'yyyy-MM-dd'
8855
+ },
8856
+ signConvention: {
8857
+ type: 'string',
8858
+ enum: ['negative-expense', 'positive-expense', 'separate-columns']
8859
+ },
8860
+ creditColumn: {
8861
+ type: 'string'
8862
+ },
8863
+ debitColumn: {
8864
+ type: 'string'
8865
+ }
8866
+ },
8867
+ required: ['columnName']
8868
+ } as const;
8869
+
8870
+ export const $ParserContributionFieldHintsDto = {
8871
+ type: 'object',
8872
+ properties: {
8873
+ date: {
8874
+ $ref: '#/components/schemas/FieldHintDto'
8875
+ },
8876
+ amount: {
8877
+ $ref: '#/components/schemas/FieldHintDto'
8878
+ },
8879
+ description: {
8880
+ $ref: '#/components/schemas/FieldHintDto'
8881
+ },
8882
+ balance: {
8883
+ $ref: '#/components/schemas/FieldHintDto'
8884
+ },
8885
+ payee: {
8886
+ $ref: '#/components/schemas/FieldHintDto'
8887
+ },
8888
+ reference: {
8889
+ $ref: '#/components/schemas/FieldHintDto'
8890
+ },
8891
+ category: {
8892
+ $ref: '#/components/schemas/FieldHintDto'
8893
+ }
8894
+ },
8895
+ required: ['date', 'amount']
8896
+ } as const;
8897
+
8898
+ export const $ExpectedTransactionDto = {
8899
+ type: 'object',
8900
+ properties: {
8901
+ date: {
8902
+ type: 'string',
8903
+ example: '2026-08-01'
8904
+ },
8905
+ amount: {
8906
+ type: 'number',
8907
+ example: -45.5
8908
+ },
8909
+ description: {
8910
+ type: 'string',
8911
+ example: '星巴克-***店'
8912
+ },
8913
+ payee: {
8914
+ type: 'string'
8915
+ },
8916
+ category: {
8917
+ type: 'string'
8918
+ }
8919
+ },
8920
+ required: ['date', 'amount', 'description']
8921
+ } as const;
8922
+
8923
+ export const $ParserContributionExamplesDto = {
8924
+ type: 'object',
8925
+ properties: {
8926
+ expectedTransactions: {
8927
+ type: 'array',
8928
+ items: {
8929
+ $ref: '#/components/schemas/ExpectedTransactionDto'
8930
+ }
8931
+ }
8932
+ },
8933
+ required: ['expectedTransactions']
8934
+ } as const;
8935
+
8936
+ export const $ParserContributionRequestDto = {
8937
+ type: 'object',
8938
+ properties: {
8939
+ meta: {
8940
+ $ref: '#/components/schemas/ParserContributionMetaDto'
8941
+ },
8942
+ samples: {
8943
+ $ref: '#/components/schemas/ParserContributionSamplesDto'
8944
+ },
8945
+ fieldHints: {
8946
+ $ref: '#/components/schemas/ParserContributionFieldHintsDto'
8947
+ },
8948
+ examples: {
8949
+ description: 'Omitted entirely by the client when empty',
8950
+ allOf: [
8951
+ {
8952
+ $ref: '#/components/schemas/ParserContributionExamplesDto'
8953
+ }
8954
+ ]
8955
+ }
8956
+ },
8957
+ required: ['meta', 'samples', 'fieldHints']
8958
+ } as const;
8959
+
8960
+ export const $ParserContributionRelayResponseDto = {
8961
+ type: 'object',
8962
+ properties: {
8963
+ issueUrl: {
8964
+ type: 'string',
8965
+ example: 'https://github.com/fire-zu/firela-vlt/issues/42'
8966
+ },
8967
+ issueNumber: {
8968
+ type: 'number',
8969
+ example: 42
8970
+ }
8971
+ },
8972
+ required: ['issueUrl', 'issueNumber']
8973
+ } as const;
8974
+
8975
+ export const $SymbolSearchResultDto = {
8976
+ type: 'object',
8977
+ properties: {
8978
+ symbol: {
8979
+ type: 'string',
8980
+ example: 'AAPL'
8981
+ },
8982
+ name: {
8983
+ type: 'string',
8984
+ example: 'Apple Inc.',
8985
+ nullable: true
8986
+ },
8987
+ exchange: {
8988
+ type: 'string',
8989
+ example: 'US',
8990
+ nullable: true
8991
+ },
8992
+ assetType: {
8993
+ type: 'string',
8994
+ description: 'OpenBB asset_type (e.g. stock, etf)',
8995
+ example: 'stock',
8996
+ nullable: true
8997
+ },
8998
+ assetClass: {
8999
+ type: 'string',
9000
+ description: 'IGN asset class (region.types.ts ASSET_CLASSES)',
9001
+ example: 'EQUITY',
9002
+ nullable: true
9003
+ },
9004
+ assetSubClass: {
9005
+ type: 'string',
9006
+ description: 'IGN asset sub-class (region.types.ts ASSET_SUB_CLASSES)',
9007
+ example: 'STOCK',
9008
+ nullable: true
9009
+ },
9010
+ currency: {
9011
+ type: 'string',
9012
+ description: 'Trading currency (extra_data or inferred from exchange)',
9013
+ example: 'USD',
9014
+ nullable: true
9015
+ }
9016
+ },
9017
+ required: ['symbol']
9018
+ } as const;
9019
+
9020
+ export const $SymbolQuoteDto = {
9021
+ type: 'object',
9022
+ properties: {
9023
+ symbol: {
9024
+ type: 'string',
9025
+ example: 'AAPL'
9026
+ },
9027
+ name: {
9028
+ type: 'string',
9029
+ example: 'Apple Inc.',
9030
+ nullable: true
9031
+ },
9032
+ exchange: {
9033
+ type: 'string',
9034
+ example: 'US',
9035
+ nullable: true
9036
+ },
9037
+ assetType: {
9038
+ type: 'string',
9039
+ description: 'OpenBB asset_type',
9040
+ example: 'stock',
9041
+ nullable: true
9042
+ },
9043
+ assetClass: {
9044
+ type: 'string',
9045
+ description: 'IGN asset class',
9046
+ example: 'EQUITY',
9047
+ nullable: true
9048
+ },
9049
+ assetSubClass: {
9050
+ type: 'string',
9051
+ description: 'IGN asset sub-class',
9052
+ example: 'STOCK',
9053
+ nullable: true
9054
+ },
9055
+ currency: {
9056
+ type: 'string',
9057
+ description: 'Trading currency (extra_data or inferred from exchange)',
9058
+ example: 'USD',
9059
+ nullable: true
9060
+ },
9061
+ price: {
9062
+ type: 'string',
9063
+ description: 'Latest price (Decimal string)',
9064
+ example: '189.84',
9065
+ nullable: true
9066
+ },
9067
+ priceDate: {
9068
+ type: 'string',
9069
+ description: 'Date the price was observed (ISO yyyy-MM-dd)',
9070
+ example: '2026-08-05',
9071
+ nullable: true
9072
+ },
9073
+ changePercent: {
9074
+ type: 'number',
9075
+ description:
9076
+ '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.',
9077
+ example: 1.7,
9078
+ nullable: true
9079
+ },
9080
+ prevClose: {
9081
+ type: 'string',
9082
+ description: 'Previous close (Decimal string)',
9083
+ nullable: true
9084
+ },
9085
+ open: {
9086
+ type: 'string',
9087
+ description: 'Day open (Decimal string)',
9088
+ nullable: true
9089
+ },
9090
+ high: {
9091
+ type: 'string',
9092
+ description: 'Day high (Decimal string)',
9093
+ nullable: true
9094
+ },
9095
+ low: {
9096
+ type: 'string',
9097
+ description: 'Day low (Decimal string)',
9098
+ nullable: true
9099
+ },
9100
+ volume: {
9101
+ type: 'string',
9102
+ description: 'Day volume (Decimal string)',
9103
+ nullable: true
9104
+ },
9105
+ yearHigh: {
9106
+ type: 'string',
9107
+ description: '52-week high (Decimal string)',
9108
+ nullable: true
9109
+ },
9110
+ yearLow: {
9111
+ type: 'string',
9112
+ description: '52-week low (Decimal string)',
9113
+ nullable: true
9114
+ }
9115
+ }
9116
+ } as const;