@firela/api-types 0.0.0-canary.3550df41 → 0.0.0-canary.35c6bcd5

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:
@@ -124,6 +132,8 @@ export const $AccountResponseDto = {
124
132
  'STUDENT_LOAN',
125
133
  'CREDIT_CARD',
126
134
  'PERSONAL_LOAN',
135
+ 'ACCOUNTS_PAYABLE',
136
+ 'TAX_PAYABLE',
127
137
  'OTHER'
128
138
  ],
129
139
  nullable: true,
@@ -179,7 +189,8 @@ export const $AccountResponseDto = {
179
189
  },
180
190
  displayName: {
181
191
  type: 'string',
182
- 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)',
183
194
  example: 'Checking'
184
195
  },
185
196
  icon: {
@@ -195,7 +206,7 @@ export const $AccountResponseDto = {
195
206
  }
196
207
  },
197
208
  platformId: {
198
- type: 'object',
209
+ type: 'string',
199
210
  description: 'Platform ID (null if unbound)',
200
211
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
201
212
  },
@@ -275,6 +286,14 @@ export const $UpdateAccountDto = {
275
286
  description: 'Icon identifier',
276
287
  example: 'bank-custom'
277
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
+ },
278
297
  openDirectiveMeta: {
279
298
  type: 'object',
280
299
  description:
@@ -375,16 +394,43 @@ export const $AccountStandardResponseDto = {
375
394
  },
376
395
  name: {
377
396
  type: 'string',
378
- 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).',
379
399
  example: 'Housing Fund'
380
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
+ },
381
425
  description: {
382
426
  type: 'string',
383
- 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).',
384
429
  example: 'ICBC checking account for daily transactions'
385
430
  },
386
431
  tags: {
387
- 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.',
388
434
  example: ['bank', 'checking', 'primary'],
389
435
  type: 'array',
390
436
  items: {
@@ -395,9 +441,47 @@ export const $AccountStandardResponseDto = {
395
441
  type: 'string',
396
442
  description: 'Icon identifier for UI display',
397
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'
398
482
  }
399
483
  },
400
- required: ['path', 'type', 'description', 'tags', 'icon']
484
+ required: ['path', 'type', 'description', 'tags', 'icon', 'productCategory']
401
485
  } as const;
402
486
 
403
487
  export const $AccountStandardListResponseDto = {
@@ -458,7 +542,10 @@ export const $RegionConfigDto = {
458
542
  },
459
543
  locale: {
460
544
  type: 'string',
461
- 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)"
462
549
  }
463
550
  },
464
551
  required: ['currency', 'dateFormat', 'locale']
@@ -471,6 +558,12 @@ export const $RegionInfoDto = {
471
558
  type: 'string',
472
559
  example: 'de'
473
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
+ },
474
567
  displayName: {
475
568
  type: 'string',
476
569
  example: 'Germany'
@@ -489,7 +582,7 @@ export const $RegionInfoDto = {
489
582
  $ref: '#/components/schemas/RegionConfigDto'
490
583
  }
491
584
  },
492
- required: ['code', 'displayName', 'chain', 'config']
585
+ required: ['code', 'open', 'displayName', 'chain', 'config']
493
586
  } as const;
494
587
 
495
588
  export const $RegionsMetadataResponseDto = {
@@ -736,7 +829,7 @@ export const $PostingResponseDto = {
736
829
  units: {
737
830
  type: 'string',
738
831
  description:
739
- '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.',
740
833
  example: '100.50'
741
834
  },
742
835
  currency: {
@@ -1102,7 +1195,7 @@ export const $PostingDetailDto = {
1102
1195
  units: {
1103
1196
  type: 'string',
1104
1197
  description:
1105
- '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.',
1106
1199
  example: '100.50'
1107
1200
  },
1108
1201
  currency: {
@@ -1286,6 +1379,147 @@ export const $TransactionDetailDto = {
1286
1379
  ]
1287
1380
  } as const;
1288
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
+
1289
1523
  export const $BalanceByCurrencyDto = {
1290
1524
  type: 'object',
1291
1525
  properties: {
@@ -1331,7 +1565,7 @@ export const $TransactionListSummaryDto = {
1331
1565
  totalAmount: {
1332
1566
  type: 'string',
1333
1567
  description:
1334
- '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.',
1335
1569
  example: '-6000.00'
1336
1570
  },
1337
1571
  currency: {
@@ -1357,6 +1591,26 @@ export const $TransactionListSummaryDto = {
1357
1591
  required: ['totalAmount', 'currency', 'balanceByCurrency']
1358
1592
  } as const;
1359
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
+
1360
1614
  export const $TransactionListResponseDto = {
1361
1615
  type: 'object',
1362
1616
  properties: {
@@ -1364,7 +1618,7 @@ export const $TransactionListResponseDto = {
1364
1618
  description: 'List of transactions',
1365
1619
  type: 'array',
1366
1620
  items: {
1367
- $ref: '#/components/schemas/TransactionDetailDto'
1621
+ $ref: '#/components/schemas/TransactionListItemDto'
1368
1622
  }
1369
1623
  },
1370
1624
  total: {
@@ -1390,6 +1644,15 @@ export const $TransactionListResponseDto = {
1390
1644
  $ref: '#/components/schemas/TransactionListSummaryDto'
1391
1645
  }
1392
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
+ ]
1393
1656
  }
1394
1657
  },
1395
1658
  required: ['data', 'total', 'limit', 'offset']
@@ -1741,13 +2004,17 @@ export const $ReviewStatsDto = {
1741
2004
  type: 'object',
1742
2005
  description: 'Count by type'
1743
2006
  },
2007
+ resolved: {
2008
+ type: 'number',
2009
+ description: 'Current count of reviews in RESOLVED status'
2010
+ },
1744
2011
  oldestPending: {
1745
2012
  format: 'date-time',
1746
2013
  type: 'string',
1747
2014
  description: 'Oldest pending review date'
1748
2015
  }
1749
2016
  },
1750
- required: ['total', 'byType']
2017
+ required: ['total', 'byType', 'resolved']
1751
2018
  } as const;
1752
2019
 
1753
2020
  export const $DecisionOptionDto = {
@@ -2300,10 +2567,10 @@ export const $UpdatePayeeDto = {
2300
2567
  meta: {
2301
2568
  type: 'object',
2302
2569
  description:
2303
- 'Metadata for extended information (location, notes, contact info, etc.). Will merge with existing metadata.',
2570
+ 'Metadata for extended information (location, notes, contact info, etc.)',
2304
2571
  example: {
2305
2572
  location: 'Zhongguancun',
2306
- note: 'Updated note',
2573
+ note: 'Near subway station',
2307
2574
  favorite: true
2308
2575
  }
2309
2576
  },
@@ -2864,568 +3131,660 @@ export const $UpdateCommodityDto = {
2864
3131
  }
2865
3132
  } as const;
2866
3133
 
2867
- export const $CreateBeanPriceDto = {
3134
+ export const $CurrencyBalanceDto = {
2868
3135
  type: 'object',
2869
3136
  properties: {
2870
3137
  currency: {
2871
3138
  type: 'string',
2872
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2873
- example: 'USD'
2874
- },
2875
- quoteCurrency: {
2876
- type: 'string',
2877
- description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3139
+ description: 'ISO 4217 currency code',
2878
3140
  example: 'CNY'
2879
3141
  },
2880
- amount: {
2881
- type: 'number',
2882
- description:
2883
- 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
2884
- example: 175.5,
2885
- minimum: 0
2886
- },
2887
- date: {
3142
+ balance: {
2888
3143
  type: 'string',
2889
- description: 'Price date (ISO 8601 format)',
2890
- example: '2024-11-05'
2891
- },
2892
- metadata: {
2893
- type: 'object',
2894
- description:
2895
- 'Metadata (validated by Zod schema, max field lengths enforced)',
2896
- example: {
2897
- source: 'MANUAL',
2898
- note: 'Bank valuation report',
2899
- confidence: 0.95
2900
- }
3144
+ description: 'Balance amount',
3145
+ example: '500000.00'
2901
3146
  }
2902
3147
  },
2903
- required: ['currency', 'quoteCurrency', 'amount', 'date']
3148
+ required: ['currency', 'balance']
2904
3149
  } as const;
2905
3150
 
2906
- export const $PriceResponseDto = {
3151
+ export const $TimeSeriesPointDto = {
2907
3152
  type: 'object',
2908
3153
  properties: {
2909
- id: {
3154
+ date: {
2910
3155
  type: 'string',
2911
- description: 'Unique identifier',
2912
- example: 'uuid-123-456'
3156
+ description: 'Date in YYYY-MM-DD format',
3157
+ example: '2024-06-15'
2913
3158
  },
2914
- userId: {
3159
+ value: {
2915
3160
  type: 'string',
2916
- description: 'User ID (owner of the price)',
2917
- example: 'user-123'
3161
+ description: 'Value at this date (in base currency)',
3162
+ example: '500000.00'
2918
3163
  },
2919
- currency: {
3164
+ change: {
2920
3165
  type: 'string',
2921
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2922
- example: 'BTC'
3166
+ description: 'Change from previous point',
3167
+ example: '5000.00'
2923
3168
  },
2924
- quoteCurrency: {
3169
+ assets: {
2925
3170
  type: 'string',
2926
- description: 'Quote currency (pricing currency, e.g., USD, CNY)',
2927
- example: 'USD'
2928
- },
2929
- amount: {
2930
- type: 'number',
2931
- description:
2932
- 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
2933
- example: 50000
3171
+ description: 'Total assets at this date (in base currency)',
3172
+ example: '494338.00'
2934
3173
  },
2935
- date: {
3174
+ liabilities: {
2936
3175
  type: 'string',
2937
- description:
2938
- 'Price date (ISO 8601 format). Represents the date this price was valid.',
2939
- example: '2024-01-01',
2940
- format: 'date'
3176
+ description: 'Total liabilities at this date (in base currency)',
3177
+ example: '310098.00'
2941
3178
  },
2942
- meta: {
2943
- type: 'object',
2944
- description:
2945
- 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
2946
- example: {
2947
- source: 'MANUAL',
2948
- note: 'User-defined price',
2949
- confidence: 1
3179
+ byCurrency: {
3180
+ description: 'Multi-currency breakdown for this point',
3181
+ type: 'array',
3182
+ items: {
3183
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2950
3184
  }
3185
+ }
3186
+ },
3187
+ required: ['date', 'value']
3188
+ } as const;
3189
+
3190
+ export const $TrendSummaryDto = {
3191
+ type: 'object',
3192
+ properties: {
3193
+ startValue: {
3194
+ type: 'string',
3195
+ description: 'Value at start of period',
3196
+ example: '450000.00'
2951
3197
  },
2952
- createdAt: {
2953
- format: 'date-time',
3198
+ endValue: {
2954
3199
  type: 'string',
2955
- description: 'Creation timestamp',
2956
- example: '2024-11-03T10:00:00Z'
3200
+ description: 'Value at end of period',
3201
+ example: '500000.00'
2957
3202
  },
2958
- updatedAt: {
2959
- format: 'date-time',
3203
+ totalChange: {
2960
3204
  type: 'string',
2961
- description: 'Last update timestamp',
2962
- example: '2024-11-03T10:00:00Z'
3205
+ description: 'Total change over period',
3206
+ example: '50000.00'
3207
+ },
3208
+ totalChangePercentage: {
3209
+ type: 'string',
3210
+ description: 'Total change percentage',
3211
+ example: '+11.11%'
2963
3212
  }
2964
3213
  },
2965
- required: [
2966
- 'id',
2967
- 'userId',
2968
- 'currency',
2969
- 'quoteCurrency',
2970
- 'amount',
2971
- 'date',
2972
- 'meta',
2973
- 'createdAt',
2974
- 'updatedAt'
2975
- ]
3214
+ required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
2976
3215
  } as const;
2977
3216
 
2978
- export const $PriceListResponseDto = {
3217
+ export const $MultiCurrencyPointDto = {
2979
3218
  type: 'object',
2980
3219
  properties: {
2981
- items: {
2982
- description: 'List of prices',
3220
+ date: {
3221
+ type: 'string',
3222
+ description: 'Date in YYYY-MM-DD format',
3223
+ example: '2024-06-15'
3224
+ },
3225
+ byCurrency: {
3226
+ description: 'Balances by currency',
2983
3227
  type: 'array',
2984
3228
  items: {
2985
- $ref: '#/components/schemas/PriceResponseDto'
3229
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2986
3230
  }
2987
- },
2988
- total: {
2989
- type: 'number',
2990
- description: 'Total number of prices',
2991
- example: 42
2992
3231
  }
2993
3232
  },
2994
- required: ['items', 'total']
3233
+ required: ['date', 'byCurrency']
2995
3234
  } as const;
2996
3235
 
2997
- export const $UpdateBeanPriceDto = {
3236
+ export const $PortfolioTrendsResponseDto = {
2998
3237
  type: 'object',
2999
3238
  properties: {
3000
- currency: {
3001
- type: 'string',
3002
- description: 'Currency being priced'
3239
+ series: {
3240
+ description: 'Time series data points',
3241
+ type: 'array',
3242
+ items: {
3243
+ $ref: '#/components/schemas/TimeSeriesPointDto'
3244
+ }
3003
3245
  },
3004
- quoteCurrency: {
3246
+ summary: {
3247
+ description: 'Period summary',
3248
+ allOf: [
3249
+ {
3250
+ $ref: '#/components/schemas/TrendSummaryDto'
3251
+ }
3252
+ ]
3253
+ },
3254
+ period: {
3005
3255
  type: 'string',
3006
- description: 'Quote currency (pricing currency)'
3256
+ description: 'Period requested',
3257
+ example: '6m'
3007
3258
  },
3008
- amount: {
3009
- type: 'number',
3010
- description: 'Price amount (MUST be >= 0 per Beancount spec)',
3011
- minimum: 0
3259
+ granularity: {
3260
+ type: 'string',
3261
+ description: 'Data granularity',
3262
+ example: 'month'
3012
3263
  },
3013
- date: {
3264
+ currency: {
3014
3265
  type: 'string',
3015
- description: 'Price date (ISO 8601 format)'
3266
+ description: 'Base currency for converted values',
3267
+ example: 'CNY'
3016
3268
  },
3017
- metadata: {
3018
- type: 'object',
3019
- description: 'Metadata'
3269
+ byCurrency: {
3270
+ description:
3271
+ 'Multi-currency time series (each point has currency breakdown)',
3272
+ type: 'array',
3273
+ items: {
3274
+ $ref: '#/components/schemas/MultiCurrencyPointDto'
3275
+ }
3276
+ },
3277
+ warnings: {
3278
+ description: 'Exchange rate warnings',
3279
+ type: 'array',
3280
+ items: {
3281
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3282
+ }
3020
3283
  }
3021
- }
3284
+ },
3285
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3022
3286
  } as const;
3023
3287
 
3024
- export const $CreateRecurringRuleDto = {
3288
+ export const $CashFlowPointDto = {
3025
3289
  type: 'object',
3026
3290
  properties: {
3027
- name: {
3291
+ month: {
3028
3292
  type: 'string',
3029
- description: 'Rule name (unique per user)',
3030
- maxLength: 100
3293
+ description: 'Month key (YYYY-MM)',
3294
+ example: '2024-03'
3031
3295
  },
3032
- icon: {
3296
+ income: {
3033
3297
  type: 'string',
3034
- description: 'Icon emoji',
3035
- maxLength: 10
3298
+ description: 'Income in base currency (absolute, converted)',
3299
+ example: '10000.00'
3036
3300
  },
3037
- frequency: {
3301
+ expense: {
3038
3302
  type: 'string',
3039
- description: 'Recurring frequency',
3040
- enum: [
3041
- 'WEEKLY',
3042
- 'BIWEEKLY',
3043
- 'MONTHLY',
3044
- 'BIMONTHLY',
3045
- 'QUARTERLY',
3046
- 'YEARLY',
3047
- 'CUSTOM'
3048
- ]
3049
- },
3050
- expectedAmount: {
3051
- type: 'number',
3052
- description: 'Expected amount (positive number)',
3053
- minimum: 0
3054
- },
3055
- expectedDay: {
3056
- type: 'number',
3057
- description: 'Expected day of month (1-31)',
3058
- minimum: 1,
3059
- maximum: 31
3060
- },
3061
- customIntervalDays: {
3062
- type: 'number',
3063
- description: 'Custom interval in days (required for CUSTOM frequency)',
3064
- minimum: 1
3303
+ description: 'Expense in base currency (absolute, converted)',
3304
+ example: '5000.00'
3065
3305
  },
3066
- currency: {
3306
+ netSavings: {
3067
3307
  type: 'string',
3068
- description: 'Currency code',
3069
- default: 'CNY',
3070
- maxLength: 10
3071
- },
3072
- matchPayeePattern: {
3308
+ description: 'netSavings = income − expense (savings positive)',
3309
+ example: '5000.00'
3310
+ }
3311
+ },
3312
+ required: ['month', 'income', 'expense', 'netSavings']
3313
+ } as const;
3314
+
3315
+ export const $CashFlowTrendSummaryDto = {
3316
+ type: 'object',
3317
+ properties: {
3318
+ totalIncome: {
3073
3319
  type: 'string',
3074
- description: 'Payee matching pattern (supports wildcards)',
3075
- maxLength: 200
3076
- },
3077
- matchAmountTolerance: {
3078
- type: 'number',
3079
- description: 'Amount tolerance percentage (0-1)',
3080
- default: 0.075,
3081
- minimum: 0,
3082
- maximum: 1
3320
+ description: 'Total income across the period',
3321
+ example: '60000.00'
3083
3322
  },
3084
- defaultExpenseAccount: {
3323
+ totalExpense: {
3085
3324
  type: 'string',
3086
- description: 'Default expense account for auto-create',
3087
- maxLength: 200
3325
+ description: 'Total expense across the period',
3326
+ example: '30000.00'
3088
3327
  },
3089
- defaultPaymentAccount: {
3328
+ totalNetSavings: {
3090
3329
  type: 'string',
3091
- description: 'Default payment account for auto-create',
3092
- maxLength: 200
3093
- },
3094
- defaultPayee: {
3095
- type: 'string',
3096
- description: 'Default payee for auto-create',
3097
- maxLength: 200
3098
- },
3099
- autoCreate: {
3100
- type: 'boolean',
3101
- description: 'Auto-create transaction when expected date arrives',
3102
- default: false
3103
- },
3104
- startDate: {
3105
- type: 'string',
3106
- description: 'Rule start date (ISO format)'
3330
+ description: 'income − expense across the period',
3331
+ example: '30000.00'
3107
3332
  },
3108
- endDate: {
3333
+ averageMonthlyNetSavings: {
3109
3334
  type: 'string',
3110
- description: 'Rule end date (ISO format)'
3335
+ description:
3336
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3337
+ example: '5000.00'
3111
3338
  }
3112
3339
  },
3113
3340
  required: [
3114
- 'name',
3115
- 'frequency',
3116
- 'expectedAmount',
3117
- 'currency',
3118
- 'matchAmountTolerance',
3119
- 'autoCreate'
3341
+ 'totalIncome',
3342
+ 'totalExpense',
3343
+ 'totalNetSavings',
3344
+ 'averageMonthlyNetSavings'
3120
3345
  ]
3121
3346
  } as const;
3122
3347
 
3123
- export const $RecurringRuleResponseDto = {
3348
+ export const $CashFlowTrendsResponseDto = {
3124
3349
  type: 'object',
3125
3350
  properties: {
3126
- id: {
3127
- type: 'string',
3128
- description: 'Rule ID'
3351
+ series: {
3352
+ description:
3353
+ 'Monthly cash-flow series (fixed N-month window, zero-filled)',
3354
+ type: 'array',
3355
+ items: {
3356
+ $ref: '#/components/schemas/CashFlowPointDto'
3357
+ }
3129
3358
  },
3130
- userId: {
3131
- type: 'string',
3132
- description: 'User ID'
3359
+ summary: {
3360
+ description: 'Period totals',
3361
+ allOf: [
3362
+ {
3363
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
3364
+ }
3365
+ ]
3133
3366
  },
3134
- name: {
3367
+ period: {
3135
3368
  type: 'string',
3136
- description: 'Rule name'
3137
- },
3138
- icon: {
3139
- type: 'object',
3140
- description: 'Icon emoji'
3369
+ description: 'Period requested',
3370
+ example: '6m'
3141
3371
  },
3142
- frequency: {
3372
+ granularity: {
3143
3373
  type: 'string',
3144
- description: 'Recurring frequency'
3145
- },
3146
- expectedAmount: {
3147
- type: 'number',
3148
- description: 'Expected amount'
3149
- },
3150
- expectedDay: {
3151
- type: 'object',
3152
- description: 'Expected day of month'
3374
+ description: 'Data granularity (v1 returns month buckets)',
3375
+ example: 'month'
3153
3376
  },
3154
- customIntervalDays: {
3155
- type: 'object',
3156
- description: 'Custom interval in days'
3377
+ currency: {
3378
+ type: 'string',
3379
+ description: 'Base currency for converted values',
3380
+ example: 'CNY'
3157
3381
  },
3382
+ warnings: {
3383
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
3384
+ type: 'array',
3385
+ items: {
3386
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3387
+ }
3388
+ }
3389
+ },
3390
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3391
+ } as const;
3392
+
3393
+ export const $GenerateSnapshotBody = {
3394
+ type: 'object',
3395
+ properties: {}
3396
+ } as const;
3397
+
3398
+ export const $GenerateSnapshotResponse = {
3399
+ type: 'object',
3400
+ properties: {}
3401
+ } as const;
3402
+
3403
+ export const $BackfillSnapshotsBody = {
3404
+ type: 'object',
3405
+ properties: {}
3406
+ } as const;
3407
+
3408
+ export const $BackfillSnapshotsResponse = {
3409
+ type: 'object',
3410
+ properties: {}
3411
+ } as const;
3412
+
3413
+ export const $CreateBeanPriceDto = {
3414
+ type: 'object',
3415
+ properties: {
3158
3416
  currency: {
3159
3417
  type: 'string',
3160
- description: 'Currency code'
3418
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3419
+ example: 'USD'
3161
3420
  },
3162
- matchPayeePattern: {
3163
- type: 'object',
3164
- description: 'Payee matching pattern'
3421
+ quoteCurrency: {
3422
+ type: 'string',
3423
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3424
+ example: 'CNY'
3165
3425
  },
3166
- matchAmountTolerance: {
3426
+ amount: {
3167
3427
  type: 'number',
3168
- description: 'Amount tolerance percentage'
3428
+ description:
3429
+ 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
3430
+ example: 175.5,
3431
+ minimum: 0
3169
3432
  },
3170
- defaultExpenseAccount: {
3171
- type: 'object',
3172
- description: 'Default expense account'
3433
+ date: {
3434
+ type: 'string',
3435
+ description: 'Price date (ISO 8601 format)',
3436
+ example: '2024-11-05'
3173
3437
  },
3174
- defaultPaymentAccount: {
3438
+ metadata: {
3175
3439
  type: 'object',
3176
- description: 'Default payment account'
3440
+ description:
3441
+ 'Metadata (validated by Zod schema, max field lengths enforced)',
3442
+ example: {
3443
+ source: 'MANUAL',
3444
+ note: 'Bank valuation report',
3445
+ confidence: 0.95
3446
+ }
3447
+ }
3448
+ },
3449
+ required: ['currency', 'quoteCurrency', 'amount', 'date']
3450
+ } as const;
3451
+
3452
+ export const $PriceResponseDto = {
3453
+ type: 'object',
3454
+ properties: {
3455
+ id: {
3456
+ type: 'string',
3457
+ description: 'Unique identifier',
3458
+ example: 'uuid-123-456'
3177
3459
  },
3178
- defaultPayee: {
3179
- type: 'object',
3180
- description: 'Default payee'
3460
+ userId: {
3461
+ type: 'string',
3462
+ description: 'User ID (owner of the price)',
3463
+ example: 'user-123'
3181
3464
  },
3182
- isActive: {
3183
- type: 'boolean',
3184
- description: 'Whether rule is active'
3465
+ currency: {
3466
+ type: 'string',
3467
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3468
+ example: 'BTC'
3185
3469
  },
3186
- startDate: {
3470
+ quoteCurrency: {
3187
3471
  type: 'string',
3188
- description: 'Rule start date (YYYY-MM-DD)'
3472
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
3473
+ example: 'USD'
3189
3474
  },
3190
- endDate: {
3191
- type: 'object',
3192
- description: 'Rule end date (YYYY-MM-DD)'
3475
+ amount: {
3476
+ type: 'number',
3477
+ description:
3478
+ 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
3479
+ example: 50000
3193
3480
  },
3194
- autoCreate: {
3195
- type: 'boolean',
3196
- description: 'Auto-create transaction on expected date'
3481
+ date: {
3482
+ type: 'string',
3483
+ description:
3484
+ 'Price date (ISO 8601 format). Represents the date this price was valid.',
3485
+ example: '2024-01-01',
3486
+ format: 'date'
3197
3487
  },
3198
- lastOccurrence: {
3488
+ meta: {
3199
3489
  type: 'object',
3200
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3201
- },
3202
- totalCount: {
3203
- type: 'number',
3204
- description: 'Total matched transactions count'
3490
+ description:
3491
+ 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
3492
+ example: {
3493
+ source: 'MANUAL',
3494
+ note: 'User-defined price',
3495
+ confidence: 1
3496
+ }
3205
3497
  },
3206
3498
  createdAt: {
3207
3499
  format: 'date-time',
3208
3500
  type: 'string',
3209
- description: 'Created at timestamp'
3501
+ description: 'Creation timestamp',
3502
+ example: '2024-11-03T10:00:00Z'
3210
3503
  },
3211
3504
  updatedAt: {
3212
3505
  format: 'date-time',
3213
3506
  type: 'string',
3214
- description: 'Updated at timestamp'
3507
+ description: 'Last update timestamp',
3508
+ example: '2024-11-03T10:00:00Z'
3215
3509
  }
3216
3510
  },
3217
3511
  required: [
3218
3512
  'id',
3219
3513
  'userId',
3220
- 'name',
3221
- 'frequency',
3222
- 'expectedAmount',
3223
3514
  'currency',
3224
- 'matchAmountTolerance',
3225
- 'isActive',
3226
- 'startDate',
3227
- 'autoCreate',
3228
- 'totalCount',
3515
+ 'quoteCurrency',
3516
+ 'amount',
3517
+ 'date',
3518
+ 'meta',
3229
3519
  'createdAt',
3230
3520
  'updatedAt'
3231
3521
  ]
3232
3522
  } as const;
3233
3523
 
3234
- export const $CreateRuleFromTransactionDto = {
3524
+ export const $PriceListResponseDto = {
3235
3525
  type: 'object',
3236
3526
  properties: {
3237
- frequency: {
3238
- type: 'string',
3239
- description: 'Recurring frequency',
3240
- enum: [
3241
- 'WEEKLY',
3242
- 'BIWEEKLY',
3243
- 'MONTHLY',
3244
- 'BIMONTHLY',
3245
- 'QUARTERLY',
3246
- 'YEARLY',
3247
- 'CUSTOM'
3248
- ],
3249
- example: 'MONTHLY'
3527
+ items: {
3528
+ description: 'List of prices',
3529
+ type: 'array',
3530
+ items: {
3531
+ $ref: '#/components/schemas/PriceResponseDto'
3532
+ }
3250
3533
  },
3251
- name: {
3252
- type: 'string',
3253
- description: 'Optional name override (default: transaction payee)',
3254
- maxLength: 100
3534
+ total: {
3535
+ type: 'number',
3536
+ description: 'Total number of prices',
3537
+ example: 42
3538
+ }
3539
+ },
3540
+ required: ['items', 'total']
3541
+ } as const;
3542
+
3543
+ export const $UpdateBeanPriceDto = {
3544
+ type: 'object',
3545
+ properties: {
3546
+ currency: {
3547
+ type: 'string',
3548
+ description: 'Currency being priced'
3255
3549
  },
3256
- icon: {
3550
+ quoteCurrency: {
3257
3551
  type: 'string',
3258
- description: 'Optional icon emoji',
3259
- maxLength: 10
3552
+ description: 'Quote currency (pricing currency)'
3553
+ },
3554
+ amount: {
3555
+ type: 'number',
3556
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
3557
+ minimum: 0
3558
+ },
3559
+ date: {
3560
+ type: 'string',
3561
+ description: 'Price date (ISO 8601 format)'
3562
+ },
3563
+ metadata: {
3564
+ type: 'object',
3565
+ description: 'Metadata'
3566
+ }
3567
+ }
3568
+ } as const;
3569
+
3570
+ export const $DeleteOwnUserDto = {
3571
+ type: 'object',
3572
+ properties: {
3573
+ accessToken: {
3574
+ type: 'string',
3575
+ description: 'Access token for user verification',
3576
+ example: 'abc123xyz'
3260
3577
  }
3261
3578
  },
3262
- required: ['frequency']
3579
+ required: ['accessToken']
3263
3580
  } as const;
3264
3581
 
3265
- export const $RecurringRuleWithStatsResponseDto = {
3582
+ export const $UserSettingsResponseDto = {
3583
+ type: 'object',
3584
+ properties: {
3585
+ baseCurrency: {
3586
+ type: 'string',
3587
+ description:
3588
+ '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).',
3589
+ example: 'USD',
3590
+ nullable: true
3591
+ }
3592
+ },
3593
+ required: ['baseCurrency']
3594
+ } as const;
3595
+
3596
+ export const $UserResponseDto = {
3266
3597
  type: 'object',
3267
3598
  properties: {
3268
3599
  id: {
3269
3600
  type: 'string',
3270
- description: 'Rule ID'
3601
+ description: 'User ID'
3271
3602
  },
3272
- userId: {
3603
+ role: {
3273
3604
  type: 'string',
3274
- description: 'User ID'
3605
+ description: 'Assigned user role'
3275
3606
  },
3276
- name: {
3607
+ permissions: {
3608
+ description: 'Permission strings',
3609
+ type: 'array',
3610
+ items: {
3611
+ type: 'string'
3612
+ }
3613
+ },
3614
+ settings: {
3615
+ description: 'User settings',
3616
+ allOf: [
3617
+ {
3618
+ $ref: '#/components/schemas/UserSettingsResponseDto'
3619
+ }
3620
+ ]
3621
+ }
3622
+ },
3623
+ required: ['id', 'role', 'permissions', 'settings']
3624
+ } as const;
3625
+
3626
+ export const $SignupDto = {
3627
+ type: 'object',
3628
+ properties: {
3629
+ turnstileToken: {
3277
3630
  type: 'string',
3278
- description: 'Rule name'
3631
+ description:
3632
+ 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
3633
+ example: '0.abc123def456...'
3634
+ }
3635
+ }
3636
+ } as const;
3637
+
3638
+ export const $SignupResponseDto = {
3639
+ type: 'object',
3640
+ properties: {
3641
+ authToken: {
3642
+ type: 'string',
3643
+ description: 'JWT auth token',
3644
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
3279
3645
  },
3280
- icon: {
3281
- type: 'object',
3282
- description: 'Icon emoji'
3646
+ accessToken: {
3647
+ type: 'string',
3648
+ description: 'Auto-generated access token'
3283
3649
  },
3284
- frequency: {
3650
+ role: {
3285
3651
  type: 'string',
3286
- description: 'Recurring frequency'
3652
+ description: 'Assigned user role',
3653
+ enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
3654
+ }
3655
+ },
3656
+ required: ['authToken', 'accessToken', 'role']
3657
+ } as const;
3658
+
3659
+ export const $UpdateUserSettingDto = {
3660
+ type: 'object',
3661
+ properties: {
3662
+ secId: {
3663
+ type: 'number',
3664
+ description: 'Security ID'
3287
3665
  },
3288
- expectedAmount: {
3666
+ annualInterestRate: {
3289
3667
  type: 'number',
3290
- description: 'Expected amount'
3668
+ description: 'Annual interest rate',
3669
+ example: 0.05
3291
3670
  },
3292
- expectedDay: {
3293
- type: 'object',
3294
- description: 'Expected day of month'
3671
+ currency: {
3672
+ type: 'string',
3673
+ description: 'Currency code',
3674
+ example: 'USD'
3295
3675
  },
3296
- customIntervalDays: {
3297
- type: 'object',
3298
- description: 'Custom interval in days'
3676
+ baseCurrency: {
3677
+ type: 'string',
3678
+ description: 'Base currency code',
3679
+ example: 'USD'
3299
3680
  },
3300
- currency: {
3681
+ benchmark: {
3301
3682
  type: 'string',
3302
- description: 'Currency code'
3683
+ description: 'Benchmark symbol',
3684
+ example: 'SPY'
3303
3685
  },
3304
- matchPayeePattern: {
3305
- type: 'object',
3306
- description: 'Payee matching pattern'
3686
+ colorScheme: {
3687
+ type: 'string',
3688
+ description: 'Color scheme',
3689
+ enum: ['DARK', 'LIGHT']
3307
3690
  },
3308
- matchAmountTolerance: {
3309
- type: 'number',
3310
- description: 'Amount tolerance percentage'
3691
+ dateRange: {
3692
+ type: 'string',
3693
+ description: 'Date range filter',
3694
+ example: '1y'
3311
3695
  },
3312
- defaultExpenseAccount: {
3313
- type: 'object',
3314
- description: 'Default expense account'
3696
+ emergencyFund: {
3697
+ type: 'number',
3698
+ description: 'Emergency fund amount',
3699
+ example: 10000
3315
3700
  },
3316
- defaultPaymentAccount: {
3317
- type: 'object',
3318
- description: 'Default payment account'
3701
+ 'filters.accounts': {
3702
+ description: 'Account filter IDs',
3703
+ type: 'array',
3704
+ items: {
3705
+ type: 'string'
3706
+ }
3319
3707
  },
3320
- defaultPayee: {
3321
- type: 'object',
3322
- description: 'Default payee'
3708
+ 'filters.assetClasses': {
3709
+ description: 'Asset class filters',
3710
+ type: 'array',
3711
+ items: {
3712
+ type: 'string'
3713
+ }
3323
3714
  },
3324
- isActive: {
3325
- type: 'boolean',
3326
- description: 'Whether rule is active'
3715
+ 'filters.dataSource': {
3716
+ type: 'string',
3717
+ description: 'Data source filter'
3327
3718
  },
3328
- startDate: {
3719
+ 'filters.symbol': {
3329
3720
  type: 'string',
3330
- description: 'Rule start date (YYYY-MM-DD)'
3721
+ description: 'Symbol filter'
3331
3722
  },
3332
- endDate: {
3333
- type: 'object',
3334
- description: 'Rule end date (YYYY-MM-DD)'
3723
+ 'filters.tags': {
3724
+ description: 'Tag filters',
3725
+ type: 'array',
3726
+ items: {
3727
+ type: 'string'
3728
+ }
3335
3729
  },
3336
- autoCreate: {
3730
+ isExperimentalFeatures: {
3337
3731
  type: 'boolean',
3338
- description: 'Auto-create transaction on expected date'
3339
- },
3340
- lastOccurrence: {
3341
- type: 'object',
3342
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3732
+ description: 'Enable experimental features'
3343
3733
  },
3344
- totalCount: {
3345
- type: 'number',
3346
- description: 'Total matched transactions count'
3734
+ isRestrictedView: {
3735
+ type: 'boolean',
3736
+ description: 'Enable restricted view mode'
3347
3737
  },
3348
- createdAt: {
3349
- format: 'date-time',
3738
+ language: {
3350
3739
  type: 'string',
3351
- description: 'Created at timestamp'
3740
+ description: 'Language code',
3741
+ example: 'en'
3352
3742
  },
3353
- updatedAt: {
3354
- format: 'date-time',
3743
+ locale: {
3355
3744
  type: 'string',
3356
- description: 'Updated at timestamp'
3357
- },
3358
- pendingCount: {
3359
- type: 'number',
3360
- description: 'Number of pending expected transactions'
3361
- },
3362
- overdueCount: {
3363
- type: 'number',
3364
- description: 'Number of overdue expected transactions'
3365
- },
3366
- nextExpectedDate: {
3367
- type: 'object',
3368
- description: 'Next expected date (YYYY-MM-DD)'
3745
+ description: 'Locale code',
3746
+ example: 'en-US'
3369
3747
  },
3370
- totalAmount: {
3748
+ projectedTotalAmount: {
3371
3749
  type: 'number',
3372
- description: 'Total amount of all matched transactions'
3750
+ description: 'Projected total amount',
3751
+ example: 1000000
3373
3752
  },
3374
- averageAmount: {
3375
- type: 'number',
3376
- description: 'Average amount per transaction'
3753
+ retirementDate: {
3754
+ type: 'string',
3755
+ description: 'Retirement date in ISO 8601 format',
3756
+ example: '2050-01-01'
3377
3757
  },
3378
- transactionCount: {
3758
+ savingsRate: {
3379
3759
  type: 'number',
3380
- description: 'Number of matched transactions'
3760
+ description: 'Savings rate percentage',
3761
+ example: 0.2
3381
3762
  },
3382
- firstDate: {
3383
- type: 'object',
3384
- description: 'First matched transaction date (YYYY-MM-DD)'
3385
- },
3386
- lastDate: {
3387
- type: 'object',
3388
- description: 'Last matched transaction date (YYYY-MM-DD)'
3389
- },
3390
- variance: {
3391
- type: 'number',
3392
- description: 'Amount variance (standard deviation squared)'
3393
- },
3394
- upcomingCount: {
3395
- type: 'number',
3396
- description: 'Number of upcoming expected transactions'
3763
+ viewMode: {
3764
+ type: 'string',
3765
+ description: 'View mode',
3766
+ enum: ['DEFAULT', 'ZEN']
3767
+ }
3768
+ }
3769
+ } as const;
3770
+
3771
+ export const $UpdatePropertyDto = {
3772
+ type: 'object',
3773
+ properties: {
3774
+ value: {
3775
+ type: 'string',
3776
+ description: 'Property value'
3397
3777
  }
3398
3778
  },
3399
- required: [
3400
- 'id',
3401
- 'userId',
3402
- 'name',
3403
- 'frequency',
3404
- 'expectedAmount',
3405
- 'currency',
3406
- 'matchAmountTolerance',
3407
- 'isActive',
3408
- 'startDate',
3409
- 'autoCreate',
3410
- 'totalCount',
3411
- 'createdAt',
3412
- 'updatedAt',
3413
- 'pendingCount',
3414
- 'overdueCount',
3415
- 'totalAmount',
3416
- 'averageAmount',
3417
- 'transactionCount',
3418
- 'variance',
3419
- 'upcomingCount'
3420
- ]
3779
+ required: ['value']
3421
3780
  } as const;
3422
3781
 
3423
- export const $UpdateRecurringRuleDto = {
3782
+ export const $CreateRecurringRuleDto = {
3424
3783
  type: 'object',
3425
3784
  properties: {
3426
3785
  name: {
3427
3786
  type: 'string',
3428
- description: 'Rule name',
3787
+ description: 'Rule name (unique per user)',
3429
3788
  maxLength: 100
3430
3789
  },
3431
3790
  icon: {
@@ -3448,7 +3807,7 @@ export const $UpdateRecurringRuleDto = {
3448
3807
  },
3449
3808
  expectedAmount: {
3450
3809
  type: 'number',
3451
- description: 'Expected amount',
3810
+ description: 'Expected amount (positive number)',
3452
3811
  minimum: 0
3453
3812
  },
3454
3813
  expectedDay: {
@@ -3459,7 +3818,7 @@ export const $UpdateRecurringRuleDto = {
3459
3818
  },
3460
3819
  customIntervalDays: {
3461
3820
  type: 'number',
3462
- description: 'Custom interval in days',
3821
+ description: 'Custom interval in days (required for CUSTOM frequency)',
3463
3822
  minimum: 1
3464
3823
  },
3465
3824
  currency: {
@@ -3469,118 +3828,136 @@ export const $UpdateRecurringRuleDto = {
3469
3828
  },
3470
3829
  matchPayeePattern: {
3471
3830
  type: 'string',
3472
- description: 'Payee matching pattern',
3831
+ description: 'Payee matching pattern (supports wildcards)',
3473
3832
  maxLength: 200
3474
3833
  },
3475
3834
  matchAmountTolerance: {
3476
3835
  type: 'number',
3477
3836
  description: 'Amount tolerance percentage (0-1)',
3837
+ default: 0.075,
3478
3838
  minimum: 0,
3479
3839
  maximum: 1
3480
3840
  },
3481
3841
  defaultExpenseAccount: {
3482
3842
  type: 'string',
3483
- description: 'Default expense account',
3843
+ description: 'Default expense account for auto-create',
3484
3844
  maxLength: 200
3485
3845
  },
3486
3846
  defaultPaymentAccount: {
3487
3847
  type: 'string',
3488
- description: 'Default payment account',
3848
+ description: 'Default payment account for auto-create',
3489
3849
  maxLength: 200
3490
3850
  },
3491
3851
  defaultPayee: {
3492
3852
  type: 'string',
3493
- description: 'Default payee',
3853
+ description: 'Default payee for auto-create',
3494
3854
  maxLength: 200
3495
3855
  },
3496
3856
  autoCreate: {
3497
3857
  type: 'boolean',
3498
- description: 'Auto-create transaction'
3858
+ description: 'Auto-create transaction when expected date arrives',
3859
+ default: false
3499
3860
  },
3500
- isActive: {
3501
- type: 'boolean',
3502
- description: 'Rule active status'
3861
+ startDate: {
3862
+ type: 'string',
3863
+ description: 'Rule start date (ISO format)'
3503
3864
  },
3504
3865
  endDate: {
3505
3866
  type: 'string',
3506
3867
  description: 'Rule end date (ISO format)'
3507
3868
  }
3508
- }
3869
+ },
3870
+ required: [
3871
+ 'name',
3872
+ 'frequency',
3873
+ 'expectedAmount',
3874
+ 'matchAmountTolerance',
3875
+ 'autoCreate'
3876
+ ]
3509
3877
  } as const;
3510
3878
 
3511
- export const $ExpectedTransactionRuleDto = {
3879
+ export const $RecurringRuleResponseDto = {
3512
3880
  type: 'object',
3513
3881
  properties: {
3882
+ id: {
3883
+ type: 'string',
3884
+ description: 'Rule ID'
3885
+ },
3886
+ userId: {
3887
+ type: 'string',
3888
+ description: 'User ID'
3889
+ },
3514
3890
  name: {
3515
3891
  type: 'string',
3516
3892
  description: 'Rule name'
3517
3893
  },
3518
3894
  icon: {
3519
- type: 'object',
3520
- description: 'Rule icon'
3895
+ type: 'string',
3896
+ description: 'Icon emoji'
3521
3897
  },
3522
3898
  frequency: {
3523
3899
  type: 'string',
3524
- description: 'Rule frequency'
3900
+ description: 'Recurring frequency'
3901
+ },
3902
+ expectedAmount: {
3903
+ type: 'number',
3904
+ description: 'Expected amount'
3905
+ },
3906
+ expectedDay: {
3907
+ type: 'number',
3908
+ description: 'Expected day of month'
3909
+ },
3910
+ customIntervalDays: {
3911
+ type: 'number',
3912
+ description: 'Custom interval in days'
3525
3913
  },
3526
3914
  currency: {
3527
3915
  type: 'string',
3528
3916
  description: 'Currency code'
3529
- }
3530
- },
3531
- required: ['name', 'frequency', 'currency']
3532
- } as const;
3533
-
3534
- export const $ExpectedTransactionResponseDto = {
3535
- type: 'object',
3536
- properties: {
3537
- id: {
3538
- type: 'string',
3539
- description: 'Expected transaction ID'
3540
3917
  },
3541
- userId: {
3918
+ matchPayeePattern: {
3542
3919
  type: 'string',
3543
- description: 'User ID'
3920
+ description: 'Payee matching pattern'
3544
3921
  },
3545
- ruleId: {
3546
- type: 'string',
3547
- description: 'Associated rule ID'
3922
+ matchAmountTolerance: {
3923
+ type: 'number',
3924
+ description: 'Amount tolerance percentage'
3548
3925
  },
3549
- expectedDate: {
3926
+ defaultExpenseAccount: {
3550
3927
  type: 'string',
3551
- description: 'Expected date (YYYY-MM-DD)'
3928
+ description: 'Default expense account'
3552
3929
  },
3553
- expectedAmount: {
3554
- type: 'number',
3555
- description: 'Expected amount'
3930
+ defaultPaymentAccount: {
3931
+ type: 'string',
3932
+ description: 'Default payment account'
3556
3933
  },
3557
- status: {
3934
+ defaultPayee: {
3558
3935
  type: 'string',
3559
- description: 'Status (PENDING, COMPLETED, SKIPPED)'
3936
+ description: 'Default payee'
3560
3937
  },
3561
- matchedTransactionId: {
3562
- type: 'object',
3563
- description: 'Matched transaction ID'
3938
+ isActive: {
3939
+ type: 'boolean',
3940
+ description: 'Whether rule is active'
3564
3941
  },
3565
- matchedAt: {
3566
- type: 'object',
3567
- description: 'Match timestamp (ISO 8601)'
3942
+ startDate: {
3943
+ type: 'string',
3944
+ description: 'Rule start date (YYYY-MM-DD)'
3568
3945
  },
3569
- matchConfidence: {
3570
- type: 'object',
3571
- description: 'Match confidence score (0-1)'
3946
+ endDate: {
3947
+ type: 'string',
3948
+ description: 'Rule end date (YYYY-MM-DD)'
3572
3949
  },
3573
- isOverdue: {
3950
+ autoCreate: {
3574
3951
  type: 'boolean',
3575
- description: 'Whether this expected transaction is overdue'
3952
+ description: 'Auto-create transaction on expected date'
3576
3953
  },
3577
- rule: {
3578
- description: 'Rule information',
3579
- allOf: [
3580
- {
3581
- $ref: '#/components/schemas/ExpectedTransactionRuleDto'
3582
- }
3583
- ]
3954
+ lastOccurrence: {
3955
+ type: 'string',
3956
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
3957
+ },
3958
+ totalCount: {
3959
+ type: 'number',
3960
+ description: 'Total matched transactions count'
3584
3961
  },
3585
3962
  createdAt: {
3586
3963
  format: 'date-time',
@@ -3596,647 +3973,581 @@ export const $ExpectedTransactionResponseDto = {
3596
3973
  required: [
3597
3974
  'id',
3598
3975
  'userId',
3599
- 'ruleId',
3600
- 'expectedDate',
3976
+ 'name',
3977
+ 'frequency',
3601
3978
  'expectedAmount',
3602
- 'status',
3603
- 'isOverdue',
3604
- 'rule',
3979
+ 'currency',
3980
+ 'matchAmountTolerance',
3981
+ 'isActive',
3982
+ 'startDate',
3983
+ 'autoCreate',
3984
+ 'totalCount',
3605
3985
  'createdAt',
3606
3986
  'updatedAt'
3607
3987
  ]
3608
3988
  } as const;
3609
3989
 
3610
- export const $ExpectedTransactionListResponseDto = {
3990
+ export const $CreateRuleFromTransactionDto = {
3611
3991
  type: 'object',
3612
3992
  properties: {
3613
- items: {
3614
- type: 'array',
3615
- items: {
3616
- $ref: '#/components/schemas/ExpectedTransactionResponseDto'
3617
- }
3993
+ frequency: {
3994
+ type: 'string',
3995
+ description: 'Recurring frequency',
3996
+ enum: [
3997
+ 'WEEKLY',
3998
+ 'BIWEEKLY',
3999
+ 'MONTHLY',
4000
+ 'BIMONTHLY',
4001
+ 'QUARTERLY',
4002
+ 'YEARLY',
4003
+ 'CUSTOM'
4004
+ ],
4005
+ example: 'MONTHLY'
3618
4006
  },
3619
- total: {
3620
- type: 'number',
3621
- description: 'Total count'
3622
- }
3623
- },
3624
- required: ['items', 'total']
3625
- } as const;
3626
-
3627
- export const $ConfirmMatchDto = {
3628
- type: 'object',
3629
- properties: {
3630
- transactionId: {
4007
+ name: {
3631
4008
  type: 'string',
3632
- description: 'Transaction ID to match with'
4009
+ description: 'Optional name override (default: transaction payee)',
4010
+ maxLength: 100
4011
+ },
4012
+ icon: {
4013
+ type: 'string',
4014
+ description: 'Optional icon emoji',
4015
+ maxLength: 10
3633
4016
  }
3634
4017
  },
3635
- required: ['transactionId']
4018
+ required: ['frequency']
3636
4019
  } as const;
3637
4020
 
3638
- export const $EnterNowDto = {
4021
+ export const $RecurringRuleWithStatsResponseDto = {
3639
4022
  type: 'object',
3640
4023
  properties: {
3641
- expenseAccount: {
4024
+ id: {
3642
4025
  type: 'string',
3643
- description:
3644
- 'Override expense account (uses rule default if not provided)',
3645
- maxLength: 200
4026
+ description: 'Rule ID'
3646
4027
  },
3647
- paymentAccount: {
4028
+ userId: {
3648
4029
  type: 'string',
3649
- description:
3650
- 'Override payment account (uses rule default if not provided)',
3651
- maxLength: 200
4030
+ description: 'User ID'
3652
4031
  },
3653
- amount: {
3654
- type: 'number',
3655
- description: 'Override amount (uses expected amount if not provided)',
3656
- minimum: 0
4032
+ name: {
4033
+ type: 'string',
4034
+ description: 'Rule name'
3657
4035
  },
3658
- payee: {
4036
+ icon: {
3659
4037
  type: 'string',
3660
- description: 'Override payee (uses rule default if not provided)',
3661
- maxLength: 200
4038
+ description: 'Icon emoji'
3662
4039
  },
3663
- narration: {
4040
+ frequency: {
3664
4041
  type: 'string',
3665
- description: 'Optional narration',
3666
- maxLength: 500
3667
- }
3668
- }
3669
- } as const;
3670
-
3671
- export const $ForecastItemDto = {
3672
- type: 'object',
3673
- properties: {
3674
- rule: {
4042
+ description: 'Recurring frequency'
4043
+ },
4044
+ expectedAmount: {
4045
+ type: 'number',
4046
+ description: 'Expected amount'
4047
+ },
4048
+ expectedDay: {
4049
+ type: 'number',
4050
+ description: 'Expected day of month'
4051
+ },
4052
+ customIntervalDays: {
4053
+ type: 'number',
4054
+ description: 'Custom interval in days'
4055
+ },
4056
+ currency: {
3675
4057
  type: 'string',
3676
- description: 'Rule name',
3677
- example: 'Rent'
4058
+ description: 'Currency code'
3678
4059
  },
3679
- ruleId: {
4060
+ matchPayeePattern: {
3680
4061
  type: 'string',
3681
- description: 'Rule ID',
3682
- example: 'clx123...'
4062
+ description: 'Payee matching pattern'
3683
4063
  },
3684
- amount: {
4064
+ matchAmountTolerance: {
3685
4065
  type: 'number',
3686
- description: 'Expected amount',
3687
- example: 3000
4066
+ description: 'Amount tolerance percentage'
3688
4067
  },
3689
- date: {
4068
+ defaultExpenseAccount: {
3690
4069
  type: 'string',
3691
- description: 'Expected date (YYYY-MM-DD)',
3692
- example: '2024-04-01'
4070
+ description: 'Default expense account'
3693
4071
  },
3694
- icon: {
4072
+ defaultPaymentAccount: {
3695
4073
  type: 'string',
3696
- description: 'Rule icon emoji',
3697
- example: '🏠',
3698
- nullable: true
4074
+ description: 'Default payment account'
3699
4075
  },
3700
- currency: {
4076
+ defaultPayee: {
3701
4077
  type: 'string',
3702
- description: 'Currency code',
3703
- example: 'CNY'
3704
- }
3705
- },
3706
- required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
3707
- } as const;
3708
-
3709
- export const $MonthlyForecastDto = {
3710
- type: 'object',
3711
- properties: {
3712
- month: {
4078
+ description: 'Default payee'
4079
+ },
4080
+ isActive: {
4081
+ type: 'boolean',
4082
+ description: 'Whether rule is active'
4083
+ },
4084
+ startDate: {
3713
4085
  type: 'string',
3714
- description: 'Month (YYYY-MM)',
3715
- example: '2024-04'
4086
+ description: 'Rule start date (YYYY-MM-DD)'
3716
4087
  },
3717
- expectedOutflow: {
4088
+ endDate: {
4089
+ type: 'string',
4090
+ description: 'Rule end date (YYYY-MM-DD)'
4091
+ },
4092
+ autoCreate: {
4093
+ type: 'boolean',
4094
+ description: 'Auto-create transaction on expected date'
4095
+ },
4096
+ lastOccurrence: {
4097
+ type: 'string',
4098
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
4099
+ },
4100
+ totalCount: {
3718
4101
  type: 'number',
3719
- description: 'Total expected outflow for the month',
3720
- example: 8500
4102
+ description: 'Total matched transactions count'
3721
4103
  },
3722
- itemCount: {
4104
+ createdAt: {
4105
+ format: 'date-time',
4106
+ type: 'string',
4107
+ description: 'Created at timestamp'
4108
+ },
4109
+ updatedAt: {
4110
+ format: 'date-time',
4111
+ type: 'string',
4112
+ description: 'Updated at timestamp'
4113
+ },
4114
+ pendingCount: {
3723
4115
  type: 'number',
3724
- description: 'Number of expected transactions',
3725
- example: 3
4116
+ description: 'Number of pending expected transactions'
3726
4117
  },
3727
- byCurrency: {
3728
- type: 'object',
3729
- description: 'Breakdown by currency',
3730
- example: {
3731
- CNY: 8500,
3732
- USD: 100
3733
- }
4118
+ overdueCount: {
4119
+ type: 'number',
4120
+ description: 'Number of overdue expected transactions'
3734
4121
  },
3735
- items: {
3736
- description: 'Individual forecast items',
3737
- type: 'array',
3738
- items: {
3739
- $ref: '#/components/schemas/ForecastItemDto'
3740
- }
3741
- }
3742
- },
3743
- required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
3744
- } as const;
3745
-
3746
- export const $ForecastResponseDto = {
3747
- type: 'object',
3748
- properties: {
3749
- forecast: {
3750
- description: 'Monthly forecast data',
3751
- type: 'array',
3752
- items: {
3753
- $ref: '#/components/schemas/MonthlyForecastDto'
3754
- }
4122
+ nextExpectedDate: {
4123
+ type: 'string',
4124
+ description: 'Next expected date (YYYY-MM-DD)'
3755
4125
  },
3756
- totalOutflow: {
4126
+ totalAmount: {
3757
4127
  type: 'number',
3758
- description: 'Total expected outflow across all months',
3759
- example: 25500
4128
+ description: 'Total amount of all matched transactions'
3760
4129
  },
3761
- totalByCurrency: {
3762
- type: 'object',
3763
- description: 'Total by currency across all months',
3764
- example: {
3765
- CNY: 25500,
3766
- USD: 300
3767
- }
4130
+ averageAmount: {
4131
+ type: 'number',
4132
+ description: 'Average amount per transaction'
3768
4133
  },
3769
- rulesCount: {
4134
+ transactionCount: {
3770
4135
  type: 'number',
3771
- description: 'Number of active recurring rules included',
3772
- example: 5
4136
+ description: 'Number of matched transactions'
3773
4137
  },
3774
- periodStart: {
4138
+ firstDate: {
3775
4139
  type: 'string',
3776
- description: 'Forecast period start date',
3777
- example: '2024-04-01'
4140
+ description: 'First matched transaction date (YYYY-MM-DD)'
3778
4141
  },
3779
- periodEnd: {
4142
+ lastDate: {
3780
4143
  type: 'string',
3781
- description: 'Forecast period end date',
3782
- example: '2024-06-30'
4144
+ description: 'Last matched transaction date (YYYY-MM-DD)'
4145
+ },
4146
+ variance: {
4147
+ type: 'number',
4148
+ description: 'Amount variance (standard deviation squared)'
4149
+ },
4150
+ upcomingCount: {
4151
+ type: 'number',
4152
+ description: 'Number of upcoming expected transactions'
3783
4153
  }
3784
4154
  },
3785
4155
  required: [
3786
- 'forecast',
3787
- 'totalOutflow',
3788
- 'totalByCurrency',
3789
- 'rulesCount',
3790
- 'periodStart',
3791
- 'periodEnd'
4156
+ 'id',
4157
+ 'userId',
4158
+ 'name',
4159
+ 'frequency',
4160
+ 'expectedAmount',
4161
+ 'currency',
4162
+ 'matchAmountTolerance',
4163
+ 'isActive',
4164
+ 'startDate',
4165
+ 'autoCreate',
4166
+ 'totalCount',
4167
+ 'createdAt',
4168
+ 'updatedAt',
4169
+ 'pendingCount',
4170
+ 'overdueCount',
4171
+ 'totalAmount',
4172
+ 'averageAmount',
4173
+ 'transactionCount',
4174
+ 'variance',
4175
+ 'upcomingCount'
3792
4176
  ]
3793
4177
  } as const;
3794
4178
 
3795
- export const $CurrencyBalanceDto = {
3796
- type: 'object',
3797
- properties: {
3798
- currency: {
3799
- type: 'string',
3800
- description: 'ISO 4217 currency code',
3801
- example: 'CNY'
3802
- },
3803
- balance: {
3804
- type: 'string',
3805
- description: 'Balance amount',
3806
- example: '500000.00'
3807
- }
3808
- },
3809
- required: ['currency', 'balance']
3810
- } as const;
3811
-
3812
- export const $TimeSeriesPointDto = {
4179
+ export const $UpdateRecurringRuleDto = {
3813
4180
  type: 'object',
3814
4181
  properties: {
3815
- date: {
3816
- type: 'string',
3817
- description: 'Date in YYYY-MM-DD format',
3818
- example: '2024-06-15'
3819
- },
3820
- value: {
4182
+ name: {
3821
4183
  type: 'string',
3822
- description: 'Value at this date (in base currency)',
3823
- example: '500000.00'
3824
- },
3825
- change: {
3826
- type: 'object',
3827
- description: 'Change from previous point',
3828
- example: '5000.00'
4184
+ description: 'Rule name (unique per user)',
4185
+ maxLength: 100
3829
4186
  },
3830
- assets: {
4187
+ icon: {
3831
4188
  type: 'string',
3832
- description: 'Total assets at this date (in base currency)',
3833
- example: '494338.00'
4189
+ description: 'Icon emoji',
4190
+ maxLength: 10
3834
4191
  },
3835
- liabilities: {
4192
+ frequency: {
3836
4193
  type: 'string',
3837
- description: 'Total liabilities at this date (in base currency)',
3838
- example: '310098.00'
4194
+ description: 'Recurring frequency',
4195
+ enum: [
4196
+ 'WEEKLY',
4197
+ 'BIWEEKLY',
4198
+ 'MONTHLY',
4199
+ 'BIMONTHLY',
4200
+ 'QUARTERLY',
4201
+ 'YEARLY',
4202
+ 'CUSTOM'
4203
+ ]
3839
4204
  },
3840
- byCurrency: {
3841
- description: 'Multi-currency breakdown for this point',
3842
- type: 'array',
3843
- items: {
3844
- $ref: '#/components/schemas/CurrencyBalanceDto'
3845
- }
3846
- }
3847
- },
3848
- required: ['date', 'value']
3849
- } as const;
3850
-
3851
- export const $TrendSummaryDto = {
3852
- type: 'object',
3853
- properties: {
3854
- startValue: {
3855
- type: 'string',
3856
- description: 'Value at start of period',
3857
- example: '450000.00'
4205
+ expectedAmount: {
4206
+ type: 'number',
4207
+ description: 'Expected amount (positive number)',
4208
+ minimum: 0
3858
4209
  },
3859
- endValue: {
3860
- type: 'string',
3861
- description: 'Value at end of period',
3862
- example: '500000.00'
4210
+ expectedDay: {
4211
+ type: 'number',
4212
+ description: 'Expected day of month (1-31)',
4213
+ minimum: 1,
4214
+ maximum: 31
3863
4215
  },
3864
- totalChange: {
4216
+ currency: {
3865
4217
  type: 'string',
3866
- description: 'Total change over period',
3867
- example: '50000.00'
4218
+ description: 'Currency code',
4219
+ maxLength: 10
3868
4220
  },
3869
- totalChangePercentage: {
3870
- type: 'string',
3871
- description: 'Total change percentage',
3872
- example: '+11.11%'
3873
- }
3874
- },
3875
- required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
3876
- } as const;
3877
-
3878
- export const $MultiCurrencyPointDto = {
3879
- type: 'object',
3880
- properties: {
3881
- date: {
4221
+ matchPayeePattern: {
3882
4222
  type: 'string',
3883
- description: 'Date in YYYY-MM-DD format',
3884
- example: '2024-06-15'
3885
- },
3886
- byCurrency: {
3887
- description: 'Balances by currency',
3888
- type: 'array',
3889
- items: {
3890
- $ref: '#/components/schemas/CurrencyBalanceDto'
3891
- }
3892
- }
3893
- },
3894
- required: ['date', 'byCurrency']
3895
- } as const;
3896
-
3897
- export const $PortfolioTrendsResponseDto = {
3898
- type: 'object',
3899
- properties: {
3900
- series: {
3901
- description: 'Time series data points',
3902
- type: 'array',
3903
- items: {
3904
- $ref: '#/components/schemas/TimeSeriesPointDto'
3905
- }
4223
+ description: 'Payee matching pattern (supports wildcards)',
4224
+ maxLength: 200
3906
4225
  },
3907
- summary: {
3908
- description: 'Period summary',
3909
- allOf: [
3910
- {
3911
- $ref: '#/components/schemas/TrendSummaryDto'
3912
- }
3913
- ]
4226
+ matchAmountTolerance: {
4227
+ type: 'number',
4228
+ description: 'Amount tolerance percentage (0-1)',
4229
+ default: 0.075,
4230
+ minimum: 0,
4231
+ maximum: 1
3914
4232
  },
3915
- period: {
4233
+ defaultExpenseAccount: {
3916
4234
  type: 'string',
3917
- description: 'Period requested',
3918
- example: '6m'
4235
+ description: 'Default expense account for auto-create',
4236
+ maxLength: 200
3919
4237
  },
3920
- granularity: {
4238
+ defaultPaymentAccount: {
3921
4239
  type: 'string',
3922
- description: 'Data granularity',
3923
- example: 'month'
4240
+ description: 'Default payment account for auto-create',
4241
+ maxLength: 200
3924
4242
  },
3925
- currency: {
4243
+ defaultPayee: {
3926
4244
  type: 'string',
3927
- description: 'Base currency for converted values',
3928
- example: 'CNY'
4245
+ description: 'Default payee for auto-create',
4246
+ maxLength: 200
3929
4247
  },
3930
- byCurrency: {
3931
- description:
3932
- 'Multi-currency time series (each point has currency breakdown)',
3933
- type: 'array',
3934
- items: {
3935
- $ref: '#/components/schemas/MultiCurrencyPointDto'
3936
- }
4248
+ autoCreate: {
4249
+ type: 'boolean',
4250
+ description: 'Auto-create transaction when expected date arrives',
4251
+ default: false
3937
4252
  },
3938
- warnings: {
3939
- description: 'Exchange rate warnings',
3940
- type: 'array',
3941
- items: {
3942
- $ref: '#/components/schemas/ExchangeRateWarningDto'
3943
- }
4253
+ endDate: {
4254
+ type: 'string',
4255
+ description: 'Rule end date (ISO format)'
4256
+ },
4257
+ customIntervalDays: {
4258
+ type: 'number',
4259
+ description: 'Custom interval in days',
4260
+ minimum: 1
4261
+ },
4262
+ isActive: {
4263
+ type: 'boolean',
4264
+ description: 'Rule active status'
3944
4265
  }
3945
- },
3946
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4266
+ }
3947
4267
  } as const;
3948
4268
 
3949
- export const $CashFlowPointDto = {
4269
+ export const $ExpectedTransactionRuleDto = {
3950
4270
  type: 'object',
3951
4271
  properties: {
3952
- month: {
4272
+ name: {
3953
4273
  type: 'string',
3954
- description: 'Month key (YYYY-MM)',
3955
- example: '2024-03'
4274
+ description: 'Rule name'
3956
4275
  },
3957
- income: {
4276
+ icon: {
3958
4277
  type: 'string',
3959
- description: 'Income in base currency (absolute, converted)',
3960
- example: '10000.00'
4278
+ description: 'Rule icon'
3961
4279
  },
3962
- expense: {
4280
+ frequency: {
3963
4281
  type: 'string',
3964
- description: 'Expense in base currency (absolute, converted)',
3965
- example: '5000.00'
4282
+ description: 'Rule frequency'
3966
4283
  },
3967
- netSavings: {
4284
+ currency: {
3968
4285
  type: 'string',
3969
- description: 'netSavings = income − expense (savings positive)',
3970
- example: '5000.00'
4286
+ description: 'Currency code'
3971
4287
  }
3972
4288
  },
3973
- required: ['month', 'income', 'expense', 'netSavings']
4289
+ required: ['name', 'frequency', 'currency']
3974
4290
  } as const;
3975
4291
 
3976
- export const $CashFlowTrendSummaryDto = {
4292
+ export const $ExpectedTransactionResponseDto = {
3977
4293
  type: 'object',
3978
4294
  properties: {
3979
- totalIncome: {
4295
+ id: {
3980
4296
  type: 'string',
3981
- description: 'Total income across the period',
3982
- example: '60000.00'
4297
+ description: 'Expected transaction ID'
3983
4298
  },
3984
- totalExpense: {
4299
+ userId: {
3985
4300
  type: 'string',
3986
- description: 'Total expense across the period',
3987
- example: '30000.00'
4301
+ description: 'User ID'
3988
4302
  },
3989
- totalNetSavings: {
4303
+ ruleId: {
3990
4304
  type: 'string',
3991
- description: 'income − expense across the period',
3992
- example: '30000.00'
4305
+ description: 'Associated rule ID'
3993
4306
  },
3994
- averageMonthlyNetSavings: {
4307
+ expectedDate: {
3995
4308
  type: 'string',
3996
- description:
3997
- 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3998
- example: '5000.00'
3999
- }
4000
- },
4001
- required: [
4002
- 'totalIncome',
4003
- 'totalExpense',
4004
- 'totalNetSavings',
4005
- 'averageMonthlyNetSavings'
4006
- ]
4007
- } as const;
4008
-
4009
- export const $CashFlowTrendsResponseDto = {
4010
- type: 'object',
4011
- properties: {
4012
- series: {
4013
- description:
4014
- 'Monthly cash-flow series (fixed N-month window, zero-filled)',
4015
- type: 'array',
4016
- items: {
4017
- $ref: '#/components/schemas/CashFlowPointDto'
4018
- }
4309
+ description: 'Expected date (YYYY-MM-DD)'
4019
4310
  },
4020
- summary: {
4021
- description: 'Period totals',
4311
+ expectedAmount: {
4312
+ type: 'number',
4313
+ description: 'Expected amount'
4314
+ },
4315
+ status: {
4316
+ type: 'string',
4317
+ description: 'Status (PENDING, COMPLETED, SKIPPED)'
4318
+ },
4319
+ matchedTransactionId: {
4320
+ type: 'string',
4321
+ description: 'Matched transaction ID'
4322
+ },
4323
+ matchedAt: {
4324
+ type: 'string',
4325
+ description: 'Match timestamp (ISO 8601)'
4326
+ },
4327
+ matchConfidence: {
4328
+ type: 'number',
4329
+ description: 'Match confidence score (0-1)'
4330
+ },
4331
+ isOverdue: {
4332
+ type: 'boolean',
4333
+ description: 'Whether this expected transaction is overdue'
4334
+ },
4335
+ rule: {
4336
+ description: 'Rule information',
4022
4337
  allOf: [
4023
4338
  {
4024
- $ref: '#/components/schemas/CashFlowTrendSummaryDto'
4339
+ $ref: '#/components/schemas/ExpectedTransactionRuleDto'
4025
4340
  }
4026
4341
  ]
4027
4342
  },
4028
- period: {
4029
- type: 'string',
4030
- description: 'Period requested',
4031
- example: '6m'
4032
- },
4033
- granularity: {
4343
+ createdAt: {
4344
+ format: 'date-time',
4034
4345
  type: 'string',
4035
- description: 'Data granularity (v1 returns month buckets)',
4036
- example: 'month'
4346
+ description: 'Created at timestamp'
4037
4347
  },
4038
- currency: {
4348
+ updatedAt: {
4349
+ format: 'date-time',
4039
4350
  type: 'string',
4040
- description: 'Base currency for converted values',
4041
- example: 'CNY'
4042
- },
4043
- warnings: {
4044
- description: 'Exchange rate warnings (e.g. missing rate for a currency)',
4045
- type: 'array',
4046
- items: {
4047
- $ref: '#/components/schemas/ExchangeRateWarningDto'
4048
- }
4351
+ description: 'Updated at timestamp'
4049
4352
  }
4050
4353
  },
4051
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4052
- } as const;
4053
-
4054
- export const $GenerateSnapshotBody = {
4055
- type: 'object',
4056
- properties: {}
4057
- } as const;
4058
-
4059
- export const $GenerateSnapshotResponse = {
4060
- type: 'object',
4061
- properties: {}
4062
- } as const;
4063
-
4064
- export const $BackfillSnapshotsBody = {
4065
- type: 'object',
4066
- properties: {}
4067
- } as const;
4068
-
4069
- export const $BackfillSnapshotsResponse = {
4070
- type: 'object',
4071
- properties: {}
4354
+ required: [
4355
+ 'id',
4356
+ 'userId',
4357
+ 'ruleId',
4358
+ 'expectedDate',
4359
+ 'expectedAmount',
4360
+ 'status',
4361
+ 'isOverdue',
4362
+ 'rule',
4363
+ 'createdAt',
4364
+ 'updatedAt'
4365
+ ]
4072
4366
  } as const;
4073
4367
 
4074
- export const $DeleteOwnUserDto = {
4368
+ export const $ExpectedTransactionListResponseDto = {
4075
4369
  type: 'object',
4076
4370
  properties: {
4077
- accessToken: {
4078
- type: 'string',
4079
- description: 'Access token for user verification',
4080
- example: 'abc123xyz'
4371
+ items: {
4372
+ type: 'array',
4373
+ items: {
4374
+ $ref: '#/components/schemas/ExpectedTransactionResponseDto'
4375
+ }
4376
+ },
4377
+ total: {
4378
+ type: 'number',
4379
+ description: 'Total count'
4081
4380
  }
4082
4381
  },
4083
- required: ['accessToken']
4382
+ required: ['items', 'total']
4084
4383
  } as const;
4085
4384
 
4086
- export const $SignupDto = {
4385
+ export const $ConfirmMatchDto = {
4087
4386
  type: 'object',
4088
4387
  properties: {
4089
- turnstileToken: {
4388
+ transactionId: {
4090
4389
  type: 'string',
4091
- description:
4092
- 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4093
- example: '0.abc123def456...'
4390
+ description: 'Transaction ID to match with'
4094
4391
  }
4095
- }
4392
+ },
4393
+ required: ['transactionId']
4096
4394
  } as const;
4097
4395
 
4098
- export const $SignupResponseDto = {
4396
+ export const $EnterNowDto = {
4099
4397
  type: 'object',
4100
4398
  properties: {
4101
- authToken: {
4399
+ expenseAccount: {
4102
4400
  type: 'string',
4103
- description: 'JWT auth token',
4104
- example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
4401
+ description:
4402
+ 'Override expense account (uses rule default if not provided)',
4403
+ maxLength: 200
4105
4404
  },
4106
- accessToken: {
4405
+ paymentAccount: {
4107
4406
  type: 'string',
4108
- description: 'Auto-generated access token'
4407
+ description:
4408
+ 'Override payment account (uses rule default if not provided)',
4409
+ maxLength: 200
4410
+ },
4411
+ amount: {
4412
+ type: 'number',
4413
+ description: 'Override amount (uses expected amount if not provided)',
4414
+ minimum: 0
4415
+ },
4416
+ payee: {
4417
+ type: 'string',
4418
+ description: 'Override payee (uses rule default if not provided)',
4419
+ maxLength: 200
4109
4420
  },
4110
- role: {
4421
+ narration: {
4111
4422
  type: 'string',
4112
- description: 'Assigned user role',
4113
- enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
4423
+ description: 'Optional narration',
4424
+ maxLength: 500
4114
4425
  }
4115
- },
4116
- required: ['authToken', 'accessToken', 'role']
4426
+ }
4117
4427
  } as const;
4118
4428
 
4119
- export const $UpdateUserSettingDto = {
4429
+ export const $ForecastItemDto = {
4120
4430
  type: 'object',
4121
4431
  properties: {
4122
- secId: {
4123
- type: 'number',
4124
- description: 'Security ID'
4432
+ rule: {
4433
+ type: 'string',
4434
+ description: 'Rule name',
4435
+ example: 'Rent'
4125
4436
  },
4126
- annualInterestRate: {
4437
+ ruleId: {
4438
+ type: 'string',
4439
+ description: 'Rule ID',
4440
+ example: 'clx123...'
4441
+ },
4442
+ amount: {
4127
4443
  type: 'number',
4128
- description: 'Annual interest rate',
4129
- example: 0.05
4444
+ description: 'Expected amount',
4445
+ example: 3000
4130
4446
  },
4131
- currency: {
4447
+ date: {
4132
4448
  type: 'string',
4133
- description: 'Currency code',
4134
- example: 'USD'
4449
+ description: 'Expected date (YYYY-MM-DD)',
4450
+ example: '2024-04-01'
4135
4451
  },
4136
- baseCurrency: {
4452
+ icon: {
4137
4453
  type: 'string',
4138
- description: 'Base currency code',
4139
- example: 'USD'
4454
+ description: 'Rule icon emoji',
4455
+ example: '🏠',
4456
+ nullable: true
4140
4457
  },
4141
- benchmark: {
4458
+ currency: {
4142
4459
  type: 'string',
4143
- description: 'Benchmark symbol',
4144
- example: 'SPY'
4145
- },
4146
- colorScheme: {
4460
+ description: 'Currency code',
4461
+ example: 'CNY'
4462
+ }
4463
+ },
4464
+ required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
4465
+ } as const;
4466
+
4467
+ export const $MonthlyForecastDto = {
4468
+ type: 'object',
4469
+ properties: {
4470
+ month: {
4147
4471
  type: 'string',
4148
- description: 'Color scheme',
4149
- enum: ['DARK', 'LIGHT']
4472
+ description: 'Month (YYYY-MM)',
4473
+ example: '2024-04'
4150
4474
  },
4151
- dateRange: {
4152
- type: 'string',
4153
- description: 'Date range filter',
4154
- example: '1y'
4475
+ expectedOutflow: {
4476
+ type: 'number',
4477
+ description: 'Total expected outflow for the month',
4478
+ example: 8500
4155
4479
  },
4156
- emergencyFund: {
4480
+ itemCount: {
4157
4481
  type: 'number',
4158
- description: 'Emergency fund amount',
4159
- example: 10000
4482
+ description: 'Number of expected transactions',
4483
+ example: 3
4160
4484
  },
4161
- 'filters.accounts': {
4162
- description: 'Account filter IDs',
4163
- type: 'array',
4164
- items: {
4165
- type: 'string'
4485
+ byCurrency: {
4486
+ type: 'object',
4487
+ description: 'Breakdown by currency',
4488
+ example: {
4489
+ CNY: 8500,
4490
+ USD: 100
4166
4491
  }
4167
4492
  },
4168
- 'filters.assetClasses': {
4169
- description: 'Asset class filters',
4493
+ items: {
4494
+ description: 'Individual forecast items',
4170
4495
  type: 'array',
4171
4496
  items: {
4172
- type: 'string'
4497
+ $ref: '#/components/schemas/ForecastItemDto'
4173
4498
  }
4174
- },
4175
- 'filters.dataSource': {
4176
- type: 'string',
4177
- description: 'Data source filter'
4178
- },
4179
- 'filters.symbol': {
4180
- type: 'string',
4181
- description: 'Symbol filter'
4182
- },
4183
- 'filters.tags': {
4184
- description: 'Tag filters',
4499
+ }
4500
+ },
4501
+ required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
4502
+ } as const;
4503
+
4504
+ export const $ForecastResponseDto = {
4505
+ type: 'object',
4506
+ properties: {
4507
+ forecast: {
4508
+ description: 'Monthly forecast data',
4185
4509
  type: 'array',
4186
4510
  items: {
4187
- type: 'string'
4511
+ $ref: '#/components/schemas/MonthlyForecastDto'
4188
4512
  }
4189
4513
  },
4190
- isExperimentalFeatures: {
4191
- type: 'boolean',
4192
- description: 'Enable experimental features'
4193
- },
4194
- isRestrictedView: {
4195
- type: 'boolean',
4196
- description: 'Enable restricted view mode'
4197
- },
4198
- language: {
4199
- type: 'string',
4200
- description: 'Language code',
4201
- example: 'en'
4202
- },
4203
- locale: {
4204
- type: 'string',
4205
- description: 'Locale code',
4206
- example: 'en-US'
4207
- },
4208
- projectedTotalAmount: {
4514
+ totalOutflow: {
4209
4515
  type: 'number',
4210
- description: 'Projected total amount',
4211
- example: 1000000
4516
+ description: 'Total expected outflow across all months',
4517
+ example: 25500
4212
4518
  },
4213
- retirementDate: {
4214
- type: 'string',
4215
- description: 'Retirement date in ISO 8601 format',
4216
- example: '2050-01-01'
4519
+ totalByCurrency: {
4520
+ type: 'object',
4521
+ description: 'Total by currency across all months',
4522
+ example: {
4523
+ CNY: 25500,
4524
+ USD: 300
4525
+ }
4217
4526
  },
4218
- savingsRate: {
4527
+ rulesCount: {
4219
4528
  type: 'number',
4220
- description: 'Savings rate percentage',
4221
- example: 0.2
4529
+ description: 'Number of active recurring rules included',
4530
+ example: 5
4222
4531
  },
4223
- viewMode: {
4532
+ periodStart: {
4224
4533
  type: 'string',
4225
- description: 'View mode',
4226
- enum: ['DEFAULT', 'ZEN']
4227
- }
4228
- }
4229
- } as const;
4230
-
4231
- export const $UpdatePropertyDto = {
4232
- type: 'object',
4233
- properties: {
4234
- value: {
4534
+ description: 'Forecast period start date',
4535
+ example: '2024-04-01'
4536
+ },
4537
+ periodEnd: {
4235
4538
  type: 'string',
4236
- description: 'Property value'
4539
+ description: 'Forecast period end date',
4540
+ example: '2024-06-30'
4237
4541
  }
4238
4542
  },
4239
- required: ['value']
4543
+ required: [
4544
+ 'forecast',
4545
+ 'totalOutflow',
4546
+ 'totalByCurrency',
4547
+ 'rulesCount',
4548
+ 'periodStart',
4549
+ 'periodEnd'
4550
+ ]
4240
4551
  } as const;
4241
4552
 
4242
4553
  export const $CreateTransactionRuleDto = {
@@ -4798,7 +5109,8 @@ export const $UpdateTransactionRuleDto = {
4798
5109
  },
4799
5110
  matchLogic: {
4800
5111
  type: 'string',
4801
- enum: ['OR', 'AND']
5112
+ enum: ['OR', 'AND'],
5113
+ default: 'OR'
4802
5114
  },
4803
5115
  amountMin: {
4804
5116
  type: 'number',
@@ -4812,13 +5124,10 @@ export const $UpdateTransactionRuleDto = {
4812
5124
  },
4813
5125
  priority: {
4814
5126
  type: 'number',
5127
+ default: 50,
4815
5128
  minimum: 0,
4816
5129
  maximum: 1000
4817
5130
  },
4818
- enabled: {
4819
- type: 'boolean',
4820
- description: 'Enable or disable the rule'
4821
- },
4822
5131
  additionalTags: {
4823
5132
  items: {
4824
5133
  type: 'array'
@@ -4828,6 +5137,10 @@ export const $UpdateTransactionRuleDto = {
4828
5137
  },
4829
5138
  additionalMetadata: {
4830
5139
  type: 'object'
5140
+ },
5141
+ enabled: {
5142
+ type: 'boolean',
5143
+ description: 'Enable or disable the rule'
4831
5144
  }
4832
5145
  }
4833
5146
  } as const;
@@ -4856,36 +5169,113 @@ export const $TestRuleDto = {
4856
5169
  maxLength: 10
4857
5170
  }
4858
5171
  },
4859
- required: ['narration']
5172
+ required: ['narration']
5173
+ } as const;
5174
+
5175
+ export const $TestRuleResponseDto = {
5176
+ type: 'object',
5177
+ properties: {
5178
+ ruleId: {
5179
+ type: 'string',
5180
+ description: 'Rule ID that was tested'
5181
+ },
5182
+ matches: {
5183
+ type: 'boolean',
5184
+ description: 'Whether the rule matched the test data'
5185
+ },
5186
+ confidence: {
5187
+ type: 'number',
5188
+ description: 'Match confidence score (0-1)',
5189
+ example: 0.85
5190
+ },
5191
+ matchDetails: {
5192
+ type: 'object',
5193
+ description: 'Details of which fields matched',
5194
+ example: {
5195
+ narration: true,
5196
+ payee: false,
5197
+ categoryAccount: false
5198
+ }
5199
+ }
5200
+ },
5201
+ required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5202
+ } as const;
5203
+
5204
+ export const $CategoryCatalogEntryDto = {
5205
+ type: 'object',
5206
+ properties: {
5207
+ slug: {
5208
+ type: 'string',
5209
+ description: 'Category slug (single source-of-truth)',
5210
+ example: 'food'
5211
+ },
5212
+ scenario: {
5213
+ type: 'string',
5214
+ description: 'Display scenario group (maps to frontend picker _scenario)',
5215
+ enum: [
5216
+ 'expense',
5217
+ 'income',
5218
+ 'investment',
5219
+ 'banking',
5220
+ 'transfer',
5221
+ 'payment'
5222
+ ],
5223
+ example: 'expense'
5224
+ },
5225
+ icon: {
5226
+ type: 'string',
5227
+ description: 'Lucide icon name',
5228
+ example: 'utensils'
5229
+ },
5230
+ regions: {
5231
+ description: "Applicable regions ('*' = all, 'cn' = CN-only)",
5232
+ example: ['*'],
5233
+ type: 'array',
5234
+ items: {
5235
+ type: 'string'
5236
+ }
5237
+ },
5238
+ categoryAccounts: {
5239
+ description:
5240
+ "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.",
5241
+ example: [
5242
+ 'Expenses:Utilities:Electricity',
5243
+ 'Expenses:Utilities:Gas',
5244
+ 'Expenses:Utilities:Internet',
5245
+ 'Expenses:Utilities:Water'
5246
+ ],
5247
+ type: 'array',
5248
+ items: {
5249
+ type: 'string'
5250
+ }
5251
+ }
5252
+ },
5253
+ required: ['slug', 'scenario', 'icon', 'regions', 'categoryAccounts']
4860
5254
  } as const;
4861
5255
 
4862
- export const $TestRuleResponseDto = {
5256
+ export const $CategoryCatalogListResponseDto = {
4863
5257
  type: 'object',
4864
5258
  properties: {
4865
- ruleId: {
4866
- type: 'string',
4867
- description: 'Rule ID that was tested'
4868
- },
4869
- matches: {
4870
- type: 'boolean',
4871
- description: 'Whether the rule matched the test data'
5259
+ items: {
5260
+ description: 'Category entries (region-scoped, query-filtered)',
5261
+ type: 'array',
5262
+ items: {
5263
+ $ref: '#/components/schemas/CategoryCatalogEntryDto'
5264
+ }
4872
5265
  },
4873
- confidence: {
5266
+ total: {
4874
5267
  type: 'number',
4875
- description: 'Match confidence score (0-1)',
4876
- example: 0.85
5268
+ description:
5269
+ 'Total category entries for the region (before query filtering)',
5270
+ example: 30
4877
5271
  },
4878
- matchDetails: {
4879
- type: 'object',
4880
- description: 'Details of which fields matched',
4881
- example: {
4882
- narration: true,
4883
- payee: false,
4884
- categoryAccount: false
4885
- }
5272
+ region: {
5273
+ type: 'string',
5274
+ description: 'Region code',
5275
+ example: 'cn'
4886
5276
  }
4887
5277
  },
4888
- required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5278
+ required: ['items', 'total', 'region']
4889
5279
  } as const;
4890
5280
 
4891
5281
  export const $CreateBeanEventDto = {
@@ -5051,6 +5441,14 @@ export const $OnboardingAccountDto = {
5051
5441
  description:
5052
5442
  'Platform ID to bind the account to (references Platform.id); omit for unbound',
5053
5443
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
5444
+ },
5445
+ displayName: {
5446
+ type: 'string',
5447
+ description:
5448
+ 'User-set display name override (omit/null = keep the derived name)',
5449
+ nullable: true,
5450
+ maxLength: 50,
5451
+ example: 'Salary card'
5054
5452
  }
5055
5453
  },
5056
5454
  required: ['path', 'currency']
@@ -5630,7 +6028,8 @@ export const $UpdateMapperDefaultsDto = {
5630
6028
  type: 'string',
5631
6029
  description: 'Source account for transactions (Beancount format)',
5632
6030
  example: 'Assets:CN:Alipay:Balance',
5633
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6031
+ pattern:
6032
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5634
6033
  },
5635
6034
  currency: {
5636
6035
  type: 'string',
@@ -5644,13 +6043,15 @@ export const $UpdateMapperDefaultsDto = {
5644
6043
  type: 'string',
5645
6044
  description: 'Default expense account (optional)',
5646
6045
  example: 'Expenses:Unknown',
5647
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6046
+ pattern:
6047
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5648
6048
  },
5649
6049
  incomeAccount: {
5650
6050
  type: 'string',
5651
6051
  description: 'Default income account (optional)',
5652
6052
  example: 'Income:Unknown',
5653
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6053
+ pattern:
6054
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5654
6055
  },
5655
6056
  methodAccountMapping: {
5656
6057
  type: 'object',
@@ -5707,12 +6108,14 @@ export const $ProviderSyncConfigDto = {
5707
6108
  },
5708
6109
  defaultExpenseAccount: {
5709
6110
  type: 'string',
5710
- description: 'Default expense account for the second posting',
6111
+ description:
6112
+ 'Default expense account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5711
6113
  example: 'Expenses:Unknown'
5712
6114
  },
5713
6115
  defaultIncomeAccount: {
5714
6116
  type: 'string',
5715
- description: 'Default income account for the second posting',
6117
+ description:
6118
+ 'Default income account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5716
6119
  example: 'Income:Unknown'
5717
6120
  },
5718
6121
  filterPending: {
@@ -5727,12 +6130,7 @@ export const $ProviderSyncConfigDto = {
5727
6130
  example: 'acc_gocardless_001'
5728
6131
  }
5729
6132
  },
5730
- required: [
5731
- 'sourceAccount',
5732
- 'defaultCurrency',
5733
- 'defaultExpenseAccount',
5734
- 'defaultIncomeAccount'
5735
- ]
6133
+ required: ['sourceAccount', 'defaultCurrency']
5736
6134
  } as const;
5737
6135
 
5738
6136
  export const $ProviderSyncDto = {
@@ -5942,15 +6340,106 @@ export const $UncoveredFormatMissDto = {
5942
6340
  properties: {}
5943
6341
  } as const;
5944
6342
 
6343
+ export const $ClientParsedDataDto = {
6344
+ type: 'object',
6345
+ properties: {
6346
+ amount: {
6347
+ type: 'number',
6348
+ description: 'Transaction amount',
6349
+ example: 35
6350
+ },
6351
+ currency: {
6352
+ type: 'string',
6353
+ description: 'Currency code',
6354
+ example: 'CNY'
6355
+ },
6356
+ date: {
6357
+ type: 'string',
6358
+ description: 'Transaction date (ISO 8601)',
6359
+ example: '2026-08-15'
6360
+ },
6361
+ payee: {
6362
+ type: 'string',
6363
+ description: 'Payee/merchant name',
6364
+ example: 'Starbucks'
6365
+ },
6366
+ narration: {
6367
+ type: 'string',
6368
+ description: 'Transaction narration'
6369
+ },
6370
+ category: {
6371
+ type: 'string',
6372
+ description:
6373
+ '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.',
6374
+ example: 'food'
6375
+ },
6376
+ incomeType: {
6377
+ type: 'string',
6378
+ description: 'Income type',
6379
+ example: 'Salary'
6380
+ },
6381
+ incomeSource: {
6382
+ type: 'string',
6383
+ description: 'Income source',
6384
+ example: 'Anthropic Inc.'
6385
+ },
6386
+ symbol: {
6387
+ type: 'string',
6388
+ description: 'Security symbol code (e.g., 600519, AAPL)',
6389
+ example: 'AAPL'
6390
+ },
6391
+ quantity: {
6392
+ type: 'number',
6393
+ description: 'Quantity of shares/units',
6394
+ example: 100
6395
+ },
6396
+ price: {
6397
+ type: 'number',
6398
+ description: 'Unit price per share/unit',
6399
+ example: 1900
6400
+ },
6401
+ investmentAction: {
6402
+ type: 'string',
6403
+ description: 'Investment action',
6404
+ enum: ['buy', 'sell'],
6405
+ example: 'buy'
6406
+ },
6407
+ paymentSource: {
6408
+ type: 'string',
6409
+ description: 'Payment source: asset (default) or liability (credit card)',
6410
+ enum: ['asset', 'liability'],
6411
+ example: 'asset'
6412
+ },
6413
+ liabilityHint: {
6414
+ type: 'string',
6415
+ description: 'Liability account hint (CreditCard/Huabei/Baitiao)',
6416
+ example: 'CreditCard'
6417
+ },
6418
+ warning: {
6419
+ type: 'string',
6420
+ description:
6421
+ 'Display-only warning from the prior response; accepted but ignored.',
6422
+ example: 'Cross-currency settlement applies.'
6423
+ }
6424
+ }
6425
+ } as const;
6426
+
5945
6427
  export const $ProcessNlpDto = {
5946
6428
  type: 'object',
5947
6429
  properties: {
5948
6430
  message: {
5949
6431
  type: 'string',
5950
- description: 'Natural language text describing a transaction (Chinese)',
5951
- example: 'yesterday Starbucks spent 35 yuan',
6432
+ description:
6433
+ 'Natural language text describing a transaction. Optional when `confirm` is true (structured confirm); otherwise required.',
6434
+ example: 'Starbucks 35',
5952
6435
  maxLength: 500
5953
6436
  },
6437
+ confirm: {
6438
+ type: 'boolean',
6439
+ description:
6440
+ 'Structured confirm signal — bypasses NL confirm-word matching when true. Send parsedData field edits alongside. The NL word-list path is the fallback.',
6441
+ example: true
6442
+ },
5954
6443
  sessionId: {
5955
6444
  type: 'string',
5956
6445
  description:
@@ -5958,14 +6447,18 @@ export const $ProcessNlpDto = {
5958
6447
  example: 'session_abc123'
5959
6448
  },
5960
6449
  parsedData: {
5961
- type: 'object',
5962
6450
  description:
5963
6451
  'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
5964
6452
  example: {
5965
6453
  amount: 35,
5966
6454
  currency: 'CNY',
5967
6455
  payee: 'Starbucks'
5968
- }
6456
+ },
6457
+ allOf: [
6458
+ {
6459
+ $ref: '#/components/schemas/ClientParsedDataDto'
6460
+ }
6461
+ ]
5969
6462
  },
5970
6463
  selectedRuleId: {
5971
6464
  type: 'string',
@@ -5976,11 +6469,29 @@ export const $ProcessNlpDto = {
5976
6469
  selectedAccount: {
5977
6470
  type: 'string',
5978
6471
  description:
5979
- 'confirm_account echo-back: account path selected from the prior confirm_account response (suggestedAccount, similarAccounts[i], or a typed path). Applied directly when the session is confirming_account — no NL re-parse.',
6472
+ '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.',
5980
6473
  example: 'Expenses:Food:Coffee'
6474
+ },
6475
+ viewpointAccount: {
6476
+ type: 'string',
6477
+ description:
6478
+ '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.',
6479
+ example: 'Assets:CN:Bank:ICBC'
6480
+ },
6481
+ viewpointCategory: {
6482
+ type: 'string',
6483
+ description:
6484
+ "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.",
6485
+ example: 'Food'
6486
+ },
6487
+ viewpointFlow: {
6488
+ type: 'string',
6489
+ description:
6490
+ "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.",
6491
+ enum: ['income', 'expense'],
6492
+ example: 'expense'
5981
6493
  }
5982
- },
5983
- required: ['message']
6494
+ }
5984
6495
  } as const;
5985
6496
 
5986
6497
  export const $NlpTransactionInfoDto = {
@@ -6046,7 +6557,9 @@ export const $NlpParsedDataDto = {
6046
6557
  },
6047
6558
  category: {
6048
6559
  type: 'string',
6049
- description: 'Category'
6560
+ description:
6561
+ '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.',
6562
+ example: 'food'
6050
6563
  },
6051
6564
  incomeType: {
6052
6565
  type: 'string',
@@ -6292,6 +6805,24 @@ export const $NlpRuleConfirmationDataDto = {
6292
6805
  ]
6293
6806
  } as const;
6294
6807
 
6808
+ export const $NlpAccountCandidateDto = {
6809
+ type: 'object',
6810
+ properties: {
6811
+ path: {
6812
+ type: 'string',
6813
+ description: 'Canonical beancount account path (echo back on selection)',
6814
+ example: 'Expenses:Food:Dining'
6815
+ },
6816
+ name: {
6817
+ type: 'string',
6818
+ description:
6819
+ 'Localized display name (ADR-0114 read-time projection, user locale)',
6820
+ example: '餐饮'
6821
+ }
6822
+ },
6823
+ required: ['path', 'name']
6824
+ } as const;
6825
+
6295
6826
  export const $NlpAccountConfirmationDataDto = {
6296
6827
  type: 'object',
6297
6828
  properties: {
@@ -6307,10 +6838,11 @@ export const $NlpAccountConfirmationDataDto = {
6307
6838
  example: 'Expenses:Food:Drinks'
6308
6839
  },
6309
6840
  similarAccounts: {
6310
- description: 'Similar accounts for user selection',
6841
+ description:
6842
+ 'Similar accounts for user selection (path + localized name, #680)',
6311
6843
  type: 'array',
6312
6844
  items: {
6313
- type: 'string'
6845
+ $ref: '#/components/schemas/NlpAccountCandidateDto'
6314
6846
  }
6315
6847
  },
6316
6848
  errorMessage: {
@@ -6598,7 +7130,7 @@ export const $NlpResponseDto = {
6598
7130
  type: 'string',
6599
7131
  description:
6600
7132
  'Asset sub-type (only present when intent is "asset"). Determines which asset-related form to render.',
6601
- enum: ['transfer', 'banking', 'investment'],
7133
+ enum: ['transfer', 'banking', 'investment', 'lend', 'lend_collect'],
6602
7134
  example: 'investment'
6603
7135
  },
6604
7136
  liabilitySubType: {
@@ -6801,13 +7333,26 @@ export const $PlatformListItemDto = {
6801
7333
  suggestedSegment: {
6802
7334
  type: 'string',
6803
7335
  description:
6804
- 'Suggested path segment — canonical with first char uppercased (ACC_COMP_NAME_RE)'
7336
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6805
7337
  },
6806
7338
  logoUrl: {
6807
7339
  type: 'string',
6808
7340
  description: 'Logo URL',
6809
7341
  nullable: true
6810
7342
  },
7343
+ countryCode: {
7344
+ type: 'string',
7345
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7346
+ example: 'CN',
7347
+ nullable: true
7348
+ },
7349
+ category: {
7350
+ type: 'string',
7351
+ description:
7352
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7353
+ nullable: true,
7354
+ example: 'DigitalWallet'
7355
+ },
6811
7356
  isBound: {
6812
7357
  type: 'boolean',
6813
7358
  description: 'Whether user has accounts using this platform'
@@ -6821,6 +7366,8 @@ export const $PlatformListItemDto = {
6821
7366
  'canonical',
6822
7367
  'suggestedSegment',
6823
7368
  'logoUrl',
7369
+ 'countryCode',
7370
+ 'category',
6824
7371
  'isBound'
6825
7372
  ]
6826
7373
  } as const;
@@ -6853,59 +7400,147 @@ export const $PlatformMatchResultDto = {
6853
7400
  'OTHER'
6854
7401
  ]
6855
7402
  },
6856
- suggestedSegment: {
7403
+ suggestedSegment: {
7404
+ type: 'string',
7405
+ description:
7406
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7407
+ },
7408
+ logoUrl: {
7409
+ type: 'string',
7410
+ description: 'Logo URL',
7411
+ nullable: true
7412
+ },
7413
+ countryCode: {
7414
+ type: 'string',
7415
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7416
+ example: 'CN',
7417
+ nullable: true
7418
+ },
7419
+ category: {
7420
+ type: 'string',
7421
+ description:
7422
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7423
+ nullable: true,
7424
+ example: 'DigitalWallet'
7425
+ },
7426
+ matchType: {
7427
+ type: 'string',
7428
+ description: "How this row matched: 'exact' > 'prefix' > 'substring'",
7429
+ enum: ['exact', 'prefix', 'substring']
7430
+ }
7431
+ },
7432
+ required: [
7433
+ 'id',
7434
+ 'name',
7435
+ 'canonical',
7436
+ 'type',
7437
+ 'suggestedSegment',
7438
+ 'logoUrl',
7439
+ 'countryCode',
7440
+ 'category',
7441
+ 'matchType'
7442
+ ]
7443
+ } as const;
7444
+
7445
+ export const $PlatformMatchResponseDto = {
7446
+ type: 'object',
7447
+ properties: {
7448
+ platforms: {
7449
+ description: 'Ranked matches, best tier first (at most 10 rows)',
7450
+ type: 'array',
7451
+ items: {
7452
+ $ref: '#/components/schemas/PlatformMatchResultDto'
7453
+ }
7454
+ },
7455
+ matchType: {
7456
+ type: 'string',
7457
+ description:
7458
+ "Overall match quality — top row's tier, or 'none' when no hits",
7459
+ enum: ['none', 'exact', 'prefix', 'substring']
7460
+ },
7461
+ total: {
7462
+ type: 'number',
7463
+ description: 'Total matches before LIMIT (truncation transparency)'
7464
+ },
7465
+ hasMore: {
7466
+ type: 'boolean',
7467
+ description: 'true when total > platforms.length (more matches exist)'
7468
+ }
7469
+ },
7470
+ required: ['platforms', 'matchType', 'total', 'hasMore']
7471
+ } as const;
7472
+
7473
+ export const $PlatformStandardsPlatformDto = {
7474
+ type: 'object',
7475
+ properties: {
7476
+ id: {
7477
+ type: 'string',
7478
+ description: 'Global platform ID'
7479
+ },
7480
+ name: {
7481
+ type: 'string',
7482
+ description: 'Platform name (e.g., "ICBC")'
7483
+ },
7484
+ canonical: {
7485
+ type: 'string',
7486
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7487
+ },
7488
+ suggestedSegment: {
7489
+ type: 'string',
7490
+ description:
7491
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7492
+ },
7493
+ type: {
7494
+ type: 'string',
7495
+ description: 'Platform type',
7496
+ enum: [
7497
+ 'BANK',
7498
+ 'BROKERAGE',
7499
+ 'CRYPTO_EXCHANGE',
7500
+ 'PAYMENT',
7501
+ 'INVESTMENT',
7502
+ 'INSURANCE',
7503
+ 'OTHER'
7504
+ ]
7505
+ },
7506
+ category: {
6857
7507
  type: 'string',
6858
7508
  description:
6859
- 'Suggested path segment — canonical, already in ACCOUNT_RE format'
6860
- },
6861
- logoUrl: {
6862
- type: 'string',
6863
- description: 'Logo URL',
6864
- nullable: true
6865
- },
6866
- matchType: {
6867
- type: 'string',
6868
- description: "How this row matched: 'exact' > 'prefix' > 'substring'",
6869
- enum: ['exact', 'prefix', 'substring']
7509
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank) resolved against the final region. null = no region-aware suggestion; fall back to type.',
7510
+ nullable: true,
7511
+ example: 'Bank'
6870
7512
  }
6871
7513
  },
6872
- required: [
6873
- 'id',
6874
- 'name',
6875
- 'canonical',
6876
- 'type',
6877
- 'suggestedSegment',
6878
- 'logoUrl',
6879
- 'matchType'
6880
- ]
7514
+ required: ['id', 'name', 'canonical', 'suggestedSegment', 'type', 'category']
6881
7515
  } as const;
6882
7516
 
6883
- export const $PlatformMatchResponseDto = {
7517
+ export const $PlatformStandardsResponseDto = {
6884
7518
  type: 'object',
6885
7519
  properties: {
6886
- platforms: {
6887
- description: 'Ranked matches, best tier first (at most 10 rows)',
6888
- type: 'array',
6889
- items: {
6890
- $ref: '#/components/schemas/PlatformMatchResultDto'
6891
- }
7520
+ platform: {
7521
+ description: 'The selected platform (institution lock source)',
7522
+ allOf: [
7523
+ {
7524
+ $ref: '#/components/schemas/PlatformStandardsPlatformDto'
7525
+ }
7526
+ ]
6892
7527
  },
6893
- matchType: {
7528
+ region: {
6894
7529
  type: 'string',
6895
7530
  description:
6896
- "Overall match quality — top row's tier, or 'none' when no hits",
6897
- enum: ['none', 'exact', 'prefix', 'substring']
6898
- },
6899
- total: {
6900
- type: 'number',
6901
- description: 'Total matches before LIMIT (truncation transparency)'
7531
+ "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.",
7532
+ example: 'CN'
6902
7533
  },
6903
- hasMore: {
6904
- type: 'boolean',
6905
- description: 'true when total > platforms.length (more matches exist)'
7534
+ templates: {
7535
+ description:
7536
+ 'Candidate account-standard templates of the resolved region (groupable by productCategory client-side)',
7537
+ type: 'array',
7538
+ items: {
7539
+ $ref: '#/components/schemas/AccountStandardResponseDto'
7540
+ }
6906
7541
  }
6907
7542
  },
6908
- required: ['platforms', 'matchType', 'total', 'hasMore']
7543
+ required: ['platform', 'region', 'templates']
6909
7544
  } as const;
6910
7545
 
6911
7546
  export const $CreatePlatformDto = {
@@ -7009,7 +7644,8 @@ export const $UpdatePlatformDto = {
7009
7644
  },
7010
7645
  isActive: {
7011
7646
  type: 'boolean',
7012
- description: 'Whether the platform is active'
7647
+ description: 'Whether the platform is active',
7648
+ default: true
7013
7649
  }
7014
7650
  }
7015
7651
  } as const;
@@ -7172,7 +7808,8 @@ export const $AccountItemDto = {
7172
7808
  },
7173
7809
  displayName: {
7174
7810
  type: 'string',
7175
- description: 'Display name (last part of account path)',
7811
+ description:
7812
+ '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)',
7176
7813
  example: 'Savings'
7177
7814
  },
7178
7815
  balance: {
@@ -7208,7 +7845,8 @@ export const $PlatformGroupDto = {
7208
7845
  example: 'CMB Bank'
7209
7846
  },
7210
7847
  accounts: {
7211
- description: 'Accounts within this platform',
7848
+ description:
7849
+ 'Accounts within this platform (Assets and Liabilities rows, #696)',
7212
7850
  type: 'array',
7213
7851
  items: {
7214
7852
  $ref: '#/components/schemas/AccountItemDto'
@@ -7216,7 +7854,8 @@ export const $PlatformGroupDto = {
7216
7854
  },
7217
7855
  totalBalance: {
7218
7856
  type: 'string',
7219
- description: 'FX-converted total balance in base currency',
7857
+ description:
7858
+ 'FX-converted total balance in base currency (nets Assets + Liabilities rows; can be negative)',
7220
7859
  example: '100000.00'
7221
7860
  },
7222
7861
  balanceByCurrency: {
@@ -7235,7 +7874,7 @@ export const $PlatformGroupDto = {
7235
7874
  sharePct: {
7236
7875
  type: 'number',
7237
7876
  description:
7238
- 'Share of the grand converted total (0-100); 0 when grand total is 0',
7877
+ 'Share of the converted asset-side grand total (0-100); liability balances are excluded from the basis; 0 when grand total is 0 (#696)',
7239
7878
  example: 42.5
7240
7879
  }
7241
7880
  },
@@ -7283,7 +7922,8 @@ export const $AccountsSummaryDto = {
7283
7922
  properties: {
7284
7923
  totalAccounts: {
7285
7924
  type: 'number',
7286
- description: 'Total number of accounts'
7925
+ description:
7926
+ 'Total number of accounts (balance sheet: Assets + Liabilities, #696)'
7287
7927
  },
7288
7928
  totalPlatforms: {
7289
7929
  type: 'number',
@@ -7341,7 +7981,8 @@ export const $AccountItemWithAssetClassDto = {
7341
7981
  },
7342
7982
  displayName: {
7343
7983
  type: 'string',
7344
- description: 'Display name (last part of account path)',
7984
+ description:
7985
+ '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)',
7345
7986
  example: 'Savings'
7346
7987
  },
7347
7988
  balance: {
@@ -7827,7 +8468,7 @@ export const $MonetaryDto = {
7827
8468
  example: 'USD'
7828
8469
  },
7829
8470
  baseCcyEquivalent: {
7830
- type: 'object',
8471
+ type: 'string',
7831
8472
  description: 'Converted to user base currency (Decimal string)',
7832
8473
  example: '21600',
7833
8474
  nullable: true
@@ -7903,13 +8544,13 @@ export const $HoldingPnlRowDto = {
7903
8544
  example: 'Assets:US:Broker:AAPL'
7904
8545
  },
7905
8546
  accountCcy: {
7906
- type: 'object',
8547
+ type: 'string',
7907
8548
  description: 'Account settlement currency (ISO 4217), from cost currency',
7908
8549
  nullable: true,
7909
8550
  example: 'USD'
7910
8551
  },
7911
8552
  brokerType: {
7912
- type: 'object',
8553
+ type: 'string',
7913
8554
  description: 'Broker type derived from Platform.type',
7914
8555
  nullable: true,
7915
8556
  example: 'broker'
@@ -7930,7 +8571,7 @@ export const $HoldingPnlRowDto = {
7930
8571
  example: 'EQUITY'
7931
8572
  },
7932
8573
  assetSubClass: {
7933
- type: 'object',
8574
+ type: 'string',
7934
8575
  nullable: true,
7935
8576
  example: 'STOCK'
7936
8577
  },
@@ -7977,14 +8618,14 @@ export const $HoldingPnlRowDto = {
7977
8618
  ]
7978
8619
  },
7979
8620
  unrealizedPnlBase: {
7980
- type: 'object',
8621
+ type: 'string',
7981
8622
  description:
7982
8623
  'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
7983
8624
  nullable: true,
7984
8625
  example: '6000'
7985
8626
  },
7986
8627
  unrealizedPnlPct: {
7987
- type: 'object',
8628
+ type: 'string',
7988
8629
  description: 'Unrealized P&L % (Decimal string)',
7989
8630
  nullable: true,
7990
8631
  example: '25'
@@ -8008,7 +8649,7 @@ export const $HoldingPnlRowDto = {
8008
8649
  ]
8009
8650
  },
8010
8651
  pctOfInvestedAssets: {
8011
- type: 'object',
8652
+ type: 'string',
8012
8653
  description:
8013
8654
  'Share of invested assets % (Decimal string); only for invested chartTokens',
8014
8655
  nullable: true,
@@ -8053,15 +8694,15 @@ export const $HoldingPnlWarningDto = {
8053
8694
  ]
8054
8695
  },
8055
8696
  symbol: {
8056
- type: 'object',
8697
+ type: 'string',
8057
8698
  nullable: true
8058
8699
  },
8059
8700
  accountId: {
8060
- type: 'object',
8701
+ type: 'string',
8061
8702
  nullable: true
8062
8703
  },
8063
8704
  currency: {
8064
- type: 'object',
8705
+ type: 'string',
8065
8706
  nullable: true
8066
8707
  }
8067
8708
  },
@@ -8125,6 +8766,216 @@ export const $AnonymousLoginResponseDto = {
8125
8766
  required: ['authToken']
8126
8767
  } as const;
8127
8768
 
8769
+ export const $ParserContributionMetaDto = {
8770
+ type: 'object',
8771
+ properties: {
8772
+ institution: {
8773
+ type: 'string',
8774
+ description: 'Institution slug (lowercase kebab-case)',
8775
+ pattern: '^[a-z0-9]+(-[a-z0-9]+)*$',
8776
+ example: 'icbc'
8777
+ },
8778
+ region: {
8779
+ type: 'string',
8780
+ enum: [
8781
+ 'cn',
8782
+ 'us',
8783
+ 'de',
8784
+ 'fr',
8785
+ 'gb',
8786
+ 'hk',
8787
+ 'jp',
8788
+ 'sg',
8789
+ 'au',
8790
+ 'ca',
8791
+ 'other'
8792
+ ]
8793
+ },
8794
+ accountType: {
8795
+ type: 'string',
8796
+ enum: ['checking', 'savings', 'credit', 'debit', 'investment']
8797
+ },
8798
+ format: {
8799
+ type: 'string',
8800
+ enum: ['csv', 'xlsx', 'pdf', 'ofx', 'qif']
8801
+ },
8802
+ institutionDisplayName: {
8803
+ type: 'string',
8804
+ example: '中国工商银行'
8805
+ },
8806
+ encoding: {
8807
+ type: 'string',
8808
+ example: 'utf-8'
8809
+ },
8810
+ delimiter: {
8811
+ type: 'string',
8812
+ description: 'CSV delimiter character: ",", ";", "\\t" or "|"'
8813
+ },
8814
+ headerRows: {
8815
+ type: 'number',
8816
+ default: 1,
8817
+ description: 'Header row count; the client omits the field when it is 1'
8818
+ },
8819
+ notes: {
8820
+ type: 'string',
8821
+ maxLength: 2000
8822
+ }
8823
+ },
8824
+ required: ['institution', 'region', 'accountType', 'format']
8825
+ } as const;
8826
+
8827
+ export const $ParserContributionSamplesDto = {
8828
+ type: 'object',
8829
+ properties: {
8830
+ rows: {
8831
+ description:
8832
+ 'Client-sanitized sample rows (key = column name, value = cell)',
8833
+ type: 'array',
8834
+ items: {
8835
+ type: 'object'
8836
+ }
8837
+ },
8838
+ rawHeaders: {
8839
+ type: 'array',
8840
+ items: {
8841
+ type: 'string'
8842
+ }
8843
+ }
8844
+ },
8845
+ required: ['rows']
8846
+ } as const;
8847
+
8848
+ export const $FieldHintDto = {
8849
+ type: 'object',
8850
+ properties: {
8851
+ columnName: {
8852
+ type: 'string',
8853
+ example: '交易日期'
8854
+ },
8855
+ format: {
8856
+ type: 'string',
8857
+ description: 'Date format, e.g. yyyy-MM-dd HH:mm',
8858
+ example: 'yyyy-MM-dd'
8859
+ },
8860
+ signConvention: {
8861
+ type: 'string',
8862
+ enum: ['negative-expense', 'positive-expense', 'separate-columns']
8863
+ },
8864
+ creditColumn: {
8865
+ type: 'string'
8866
+ },
8867
+ debitColumn: {
8868
+ type: 'string'
8869
+ }
8870
+ },
8871
+ required: ['columnName']
8872
+ } as const;
8873
+
8874
+ export const $ParserContributionFieldHintsDto = {
8875
+ type: 'object',
8876
+ properties: {
8877
+ date: {
8878
+ $ref: '#/components/schemas/FieldHintDto'
8879
+ },
8880
+ amount: {
8881
+ $ref: '#/components/schemas/FieldHintDto'
8882
+ },
8883
+ description: {
8884
+ $ref: '#/components/schemas/FieldHintDto'
8885
+ },
8886
+ balance: {
8887
+ $ref: '#/components/schemas/FieldHintDto'
8888
+ },
8889
+ payee: {
8890
+ $ref: '#/components/schemas/FieldHintDto'
8891
+ },
8892
+ reference: {
8893
+ $ref: '#/components/schemas/FieldHintDto'
8894
+ },
8895
+ category: {
8896
+ $ref: '#/components/schemas/FieldHintDto'
8897
+ }
8898
+ },
8899
+ required: ['date', 'amount']
8900
+ } as const;
8901
+
8902
+ export const $ExpectedTransactionDto = {
8903
+ type: 'object',
8904
+ properties: {
8905
+ date: {
8906
+ type: 'string',
8907
+ example: '2026-08-01'
8908
+ },
8909
+ amount: {
8910
+ type: 'number',
8911
+ example: -45.5
8912
+ },
8913
+ description: {
8914
+ type: 'string',
8915
+ example: '星巴克-***店'
8916
+ },
8917
+ payee: {
8918
+ type: 'string'
8919
+ },
8920
+ category: {
8921
+ type: 'string'
8922
+ }
8923
+ },
8924
+ required: ['date', 'amount', 'description']
8925
+ } as const;
8926
+
8927
+ export const $ParserContributionExamplesDto = {
8928
+ type: 'object',
8929
+ properties: {
8930
+ expectedTransactions: {
8931
+ type: 'array',
8932
+ items: {
8933
+ $ref: '#/components/schemas/ExpectedTransactionDto'
8934
+ }
8935
+ }
8936
+ },
8937
+ required: ['expectedTransactions']
8938
+ } as const;
8939
+
8940
+ export const $ParserContributionRequestDto = {
8941
+ type: 'object',
8942
+ properties: {
8943
+ meta: {
8944
+ $ref: '#/components/schemas/ParserContributionMetaDto'
8945
+ },
8946
+ samples: {
8947
+ $ref: '#/components/schemas/ParserContributionSamplesDto'
8948
+ },
8949
+ fieldHints: {
8950
+ $ref: '#/components/schemas/ParserContributionFieldHintsDto'
8951
+ },
8952
+ examples: {
8953
+ description: 'Omitted entirely by the client when empty',
8954
+ allOf: [
8955
+ {
8956
+ $ref: '#/components/schemas/ParserContributionExamplesDto'
8957
+ }
8958
+ ]
8959
+ }
8960
+ },
8961
+ required: ['meta', 'samples', 'fieldHints']
8962
+ } as const;
8963
+
8964
+ export const $ParserContributionRelayResponseDto = {
8965
+ type: 'object',
8966
+ properties: {
8967
+ issueUrl: {
8968
+ type: 'string',
8969
+ example: 'https://github.com/fire-zu/firela-vlt/issues/42'
8970
+ },
8971
+ issueNumber: {
8972
+ type: 'number',
8973
+ example: 42
8974
+ }
8975
+ },
8976
+ required: ['issueUrl', 'issueNumber']
8977
+ } as const;
8978
+
8128
8979
  export const $SymbolSearchResultDto = {
8129
8980
  type: 'object',
8130
8981
  properties: {
@@ -8133,35 +8984,35 @@ export const $SymbolSearchResultDto = {
8133
8984
  example: 'AAPL'
8134
8985
  },
8135
8986
  name: {
8136
- type: 'object',
8987
+ type: 'string',
8137
8988
  example: 'Apple Inc.',
8138
8989
  nullable: true
8139
8990
  },
8140
8991
  exchange: {
8141
- type: 'object',
8992
+ type: 'string',
8142
8993
  example: 'US',
8143
8994
  nullable: true
8144
8995
  },
8145
8996
  assetType: {
8146
- type: 'object',
8997
+ type: 'string',
8147
8998
  description: 'OpenBB asset_type (e.g. stock, etf)',
8148
8999
  example: 'stock',
8149
9000
  nullable: true
8150
9001
  },
8151
9002
  assetClass: {
8152
- type: 'object',
9003
+ type: 'string',
8153
9004
  description: 'IGN asset class (region.types.ts ASSET_CLASSES)',
8154
9005
  example: 'EQUITY',
8155
9006
  nullable: true
8156
9007
  },
8157
9008
  assetSubClass: {
8158
- type: 'object',
9009
+ type: 'string',
8159
9010
  description: 'IGN asset sub-class (region.types.ts ASSET_SUB_CLASSES)',
8160
9011
  example: 'STOCK',
8161
9012
  nullable: true
8162
9013
  },
8163
9014
  currency: {
8164
- type: 'object',
9015
+ type: 'string',
8165
9016
  description: 'Trading currency (extra_data or inferred from exchange)',
8166
9017
  example: 'USD',
8167
9018
  nullable: true
@@ -8178,90 +9029,90 @@ export const $SymbolQuoteDto = {
8178
9029
  example: 'AAPL'
8179
9030
  },
8180
9031
  name: {
8181
- type: 'object',
9032
+ type: 'string',
8182
9033
  example: 'Apple Inc.',
8183
9034
  nullable: true
8184
9035
  },
8185
9036
  exchange: {
8186
- type: 'object',
9037
+ type: 'string',
8187
9038
  example: 'US',
8188
9039
  nullable: true
8189
9040
  },
8190
9041
  assetType: {
8191
- type: 'object',
9042
+ type: 'string',
8192
9043
  description: 'OpenBB asset_type',
8193
9044
  example: 'stock',
8194
9045
  nullable: true
8195
9046
  },
8196
9047
  assetClass: {
8197
- type: 'object',
9048
+ type: 'string',
8198
9049
  description: 'IGN asset class',
8199
9050
  example: 'EQUITY',
8200
9051
  nullable: true
8201
9052
  },
8202
9053
  assetSubClass: {
8203
- type: 'object',
9054
+ type: 'string',
8204
9055
  description: 'IGN asset sub-class',
8205
9056
  example: 'STOCK',
8206
9057
  nullable: true
8207
9058
  },
8208
9059
  currency: {
8209
- type: 'object',
9060
+ type: 'string',
8210
9061
  description: 'Trading currency (extra_data or inferred from exchange)',
8211
9062
  example: 'USD',
8212
9063
  nullable: true
8213
9064
  },
8214
9065
  price: {
8215
- type: 'object',
9066
+ type: 'string',
8216
9067
  description: 'Latest price (Decimal string)',
8217
9068
  example: '189.84',
8218
9069
  nullable: true
8219
9070
  },
8220
9071
  priceDate: {
8221
- type: 'object',
9072
+ type: 'string',
8222
9073
  description: 'Date the price was observed (ISO yyyy-MM-dd)',
8223
9074
  example: '2026-08-05',
8224
9075
  nullable: true
8225
9076
  },
8226
9077
  changePercent: {
8227
- type: 'object',
9078
+ type: 'number',
8228
9079
  description:
8229
9080
  '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.',
8230
9081
  example: 1.7,
8231
9082
  nullable: true
8232
9083
  },
8233
9084
  prevClose: {
8234
- type: 'object',
9085
+ type: 'string',
8235
9086
  description: 'Previous close (Decimal string)',
8236
9087
  nullable: true
8237
9088
  },
8238
9089
  open: {
8239
- type: 'object',
9090
+ type: 'string',
8240
9091
  description: 'Day open (Decimal string)',
8241
9092
  nullable: true
8242
9093
  },
8243
9094
  high: {
8244
- type: 'object',
9095
+ type: 'string',
8245
9096
  description: 'Day high (Decimal string)',
8246
9097
  nullable: true
8247
9098
  },
8248
9099
  low: {
8249
- type: 'object',
9100
+ type: 'string',
8250
9101
  description: 'Day low (Decimal string)',
8251
9102
  nullable: true
8252
9103
  },
8253
9104
  volume: {
8254
- type: 'object',
9105
+ type: 'string',
8255
9106
  description: 'Day volume (Decimal string)',
8256
9107
  nullable: true
8257
9108
  },
8258
9109
  yearHigh: {
8259
- type: 'object',
9110
+ type: 'string',
8260
9111
  description: '52-week high (Decimal string)',
8261
9112
  nullable: true
8262
9113
  },
8263
9114
  yearLow: {
8264
- type: 'object',
9115
+ type: 'string',
8265
9116
  description: '52-week low (Decimal string)',
8266
9117
  nullable: true
8267
9118
  }