@firela/api-types 0.0.0-canary.44b8d8ec → 0.0.0-canary.46e1f335

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:
@@ -181,7 +189,8 @@ export const $AccountResponseDto = {
181
189
  },
182
190
  displayName: {
183
191
  type: 'string',
184
- 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)',
185
194
  example: 'Checking'
186
195
  },
187
196
  icon: {
@@ -197,7 +206,7 @@ export const $AccountResponseDto = {
197
206
  }
198
207
  },
199
208
  platformId: {
200
- type: 'object',
209
+ type: 'string',
201
210
  description: 'Platform ID (null if unbound)',
202
211
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
203
212
  },
@@ -277,6 +286,14 @@ export const $UpdateAccountDto = {
277
286
  description: 'Icon identifier',
278
287
  example: 'bank-custom'
279
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
+ },
280
297
  openDirectiveMeta: {
281
298
  type: 'object',
282
299
  description:
@@ -377,16 +394,43 @@ export const $AccountStandardResponseDto = {
377
394
  },
378
395
  name: {
379
396
  type: 'string',
380
- 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).',
381
399
  example: 'Housing Fund'
382
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
+ },
383
425
  description: {
384
426
  type: 'string',
385
- 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).',
386
429
  example: 'ICBC checking account for daily transactions'
387
430
  },
388
431
  tags: {
389
- 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.',
390
434
  example: ['bank', 'checking', 'primary'],
391
435
  type: 'array',
392
436
  items: {
@@ -498,7 +542,10 @@ export const $RegionConfigDto = {
498
542
  },
499
543
  locale: {
500
544
  type: 'string',
501
- 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)"
502
549
  }
503
550
  },
504
551
  required: ['currency', 'dateFormat', 'locale']
@@ -511,6 +558,12 @@ export const $RegionInfoDto = {
511
558
  type: 'string',
512
559
  example: 'de'
513
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
+ },
514
567
  displayName: {
515
568
  type: 'string',
516
569
  example: 'Germany'
@@ -529,7 +582,7 @@ export const $RegionInfoDto = {
529
582
  $ref: '#/components/schemas/RegionConfigDto'
530
583
  }
531
584
  },
532
- required: ['code', 'displayName', 'chain', 'config']
585
+ required: ['code', 'open', 'displayName', 'chain', 'config']
533
586
  } as const;
534
587
 
535
588
  export const $RegionsMetadataResponseDto = {
@@ -776,7 +829,7 @@ export const $PostingResponseDto = {
776
829
  units: {
777
830
  type: 'string',
778
831
  description:
779
- '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.',
780
833
  example: '100.50'
781
834
  },
782
835
  currency: {
@@ -1142,7 +1195,7 @@ export const $PostingDetailDto = {
1142
1195
  units: {
1143
1196
  type: 'string',
1144
1197
  description:
1145
- '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.',
1146
1199
  example: '100.50'
1147
1200
  },
1148
1201
  currency: {
@@ -1326,6 +1379,147 @@ export const $TransactionDetailDto = {
1326
1379
  ]
1327
1380
  } as const;
1328
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
+
1329
1523
  export const $BalanceByCurrencyDto = {
1330
1524
  type: 'object',
1331
1525
  properties: {
@@ -1371,7 +1565,7 @@ export const $TransactionListSummaryDto = {
1371
1565
  totalAmount: {
1372
1566
  type: 'string',
1373
1567
  description:
1374
- '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.',
1375
1569
  example: '-6000.00'
1376
1570
  },
1377
1571
  currency: {
@@ -1397,6 +1591,26 @@ export const $TransactionListSummaryDto = {
1397
1591
  required: ['totalAmount', 'currency', 'balanceByCurrency']
1398
1592
  } as const;
1399
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
+
1400
1614
  export const $TransactionListResponseDto = {
1401
1615
  type: 'object',
1402
1616
  properties: {
@@ -1404,7 +1618,7 @@ export const $TransactionListResponseDto = {
1404
1618
  description: 'List of transactions',
1405
1619
  type: 'array',
1406
1620
  items: {
1407
- $ref: '#/components/schemas/TransactionDetailDto'
1621
+ $ref: '#/components/schemas/TransactionListItemDto'
1408
1622
  }
1409
1623
  },
1410
1624
  total: {
@@ -1430,6 +1644,15 @@ export const $TransactionListResponseDto = {
1430
1644
  $ref: '#/components/schemas/TransactionListSummaryDto'
1431
1645
  }
1432
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
+ ]
1433
1656
  }
1434
1657
  },
1435
1658
  required: ['data', 'total', 'limit', 'offset']
@@ -1781,13 +2004,17 @@ export const $ReviewStatsDto = {
1781
2004
  type: 'object',
1782
2005
  description: 'Count by type'
1783
2006
  },
2007
+ resolved: {
2008
+ type: 'number',
2009
+ description: 'Current count of reviews in RESOLVED status'
2010
+ },
1784
2011
  oldestPending: {
1785
2012
  format: 'date-time',
1786
2013
  type: 'string',
1787
2014
  description: 'Oldest pending review date'
1788
2015
  }
1789
2016
  },
1790
- required: ['total', 'byType']
2017
+ required: ['total', 'byType', 'resolved']
1791
2018
  } as const;
1792
2019
 
1793
2020
  export const $DecisionOptionDto = {
@@ -2340,10 +2567,10 @@ export const $UpdatePayeeDto = {
2340
2567
  meta: {
2341
2568
  type: 'object',
2342
2569
  description:
2343
- 'Metadata for extended information (location, notes, contact info, etc.). Will merge with existing metadata.',
2570
+ 'Metadata for extended information (location, notes, contact info, etc.)',
2344
2571
  example: {
2345
2572
  location: 'Zhongguancun',
2346
- note: 'Updated note',
2573
+ note: 'Near subway station',
2347
2574
  favorite: true
2348
2575
  }
2349
2576
  },
@@ -2904,568 +3131,660 @@ export const $UpdateCommodityDto = {
2904
3131
  }
2905
3132
  } as const;
2906
3133
 
2907
- export const $CreateBeanPriceDto = {
3134
+ export const $CurrencyBalanceDto = {
2908
3135
  type: 'object',
2909
3136
  properties: {
2910
3137
  currency: {
2911
3138
  type: 'string',
2912
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2913
- example: 'USD'
2914
- },
2915
- quoteCurrency: {
2916
- type: 'string',
2917
- description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3139
+ description: 'ISO 4217 currency code',
2918
3140
  example: 'CNY'
2919
3141
  },
2920
- amount: {
2921
- type: 'number',
2922
- description:
2923
- 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
2924
- example: 175.5,
2925
- minimum: 0
2926
- },
2927
- date: {
3142
+ balance: {
2928
3143
  type: 'string',
2929
- description: 'Price date (ISO 8601 format)',
2930
- example: '2024-11-05'
2931
- },
2932
- metadata: {
2933
- type: 'object',
2934
- description:
2935
- 'Metadata (validated by Zod schema, max field lengths enforced)',
2936
- example: {
2937
- source: 'MANUAL',
2938
- note: 'Bank valuation report',
2939
- confidence: 0.95
2940
- }
3144
+ description: 'Balance amount',
3145
+ example: '500000.00'
2941
3146
  }
2942
3147
  },
2943
- required: ['currency', 'quoteCurrency', 'amount', 'date']
3148
+ required: ['currency', 'balance']
2944
3149
  } as const;
2945
3150
 
2946
- export const $PriceResponseDto = {
3151
+ export const $TimeSeriesPointDto = {
2947
3152
  type: 'object',
2948
3153
  properties: {
2949
- id: {
3154
+ date: {
2950
3155
  type: 'string',
2951
- description: 'Unique identifier',
2952
- example: 'uuid-123-456'
3156
+ description: 'Date in YYYY-MM-DD format',
3157
+ example: '2024-06-15'
2953
3158
  },
2954
- userId: {
3159
+ value: {
2955
3160
  type: 'string',
2956
- description: 'User ID (owner of the price)',
2957
- example: 'user-123'
3161
+ description: 'Value at this date (in base currency)',
3162
+ example: '500000.00'
2958
3163
  },
2959
- currency: {
3164
+ change: {
2960
3165
  type: 'string',
2961
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2962
- example: 'BTC'
3166
+ description: 'Change from previous point',
3167
+ example: '5000.00'
2963
3168
  },
2964
- quoteCurrency: {
3169
+ assets: {
2965
3170
  type: 'string',
2966
- description: 'Quote currency (pricing currency, e.g., USD, CNY)',
2967
- example: 'USD'
3171
+ description: 'Total assets at this date (in base currency)',
3172
+ example: '494338.00'
2968
3173
  },
2969
- amount: {
2970
- type: 'number',
2971
- description:
2972
- 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
2973
- example: 50000
2974
- },
2975
- date: {
3174
+ liabilities: {
2976
3175
  type: 'string',
2977
- description:
2978
- 'Price date (ISO 8601 format). Represents the date this price was valid.',
2979
- example: '2024-01-01',
2980
- format: 'date'
3176
+ description: 'Total liabilities at this date (in base currency)',
3177
+ example: '310098.00'
2981
3178
  },
2982
- meta: {
2983
- type: 'object',
2984
- description:
2985
- 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
2986
- example: {
2987
- source: 'MANUAL',
2988
- note: 'User-defined price',
2989
- confidence: 1
3179
+ byCurrency: {
3180
+ description: 'Multi-currency breakdown for this point',
3181
+ type: 'array',
3182
+ items: {
3183
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2990
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'
2991
3197
  },
2992
- createdAt: {
2993
- format: 'date-time',
3198
+ endValue: {
2994
3199
  type: 'string',
2995
- description: 'Creation timestamp',
2996
- example: '2024-11-03T10:00:00Z'
3200
+ description: 'Value at end of period',
3201
+ example: '500000.00'
2997
3202
  },
2998
- updatedAt: {
2999
- format: 'date-time',
3203
+ totalChange: {
3000
3204
  type: 'string',
3001
- description: 'Last update timestamp',
3002
- 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%'
3003
3212
  }
3004
3213
  },
3005
- required: [
3006
- 'id',
3007
- 'userId',
3008
- 'currency',
3009
- 'quoteCurrency',
3010
- 'amount',
3011
- 'date',
3012
- 'meta',
3013
- 'createdAt',
3014
- 'updatedAt'
3015
- ]
3214
+ required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
3016
3215
  } as const;
3017
3216
 
3018
- export const $PriceListResponseDto = {
3217
+ export const $MultiCurrencyPointDto = {
3019
3218
  type: 'object',
3020
3219
  properties: {
3021
- items: {
3022
- 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',
3023
3227
  type: 'array',
3024
3228
  items: {
3025
- $ref: '#/components/schemas/PriceResponseDto'
3229
+ $ref: '#/components/schemas/CurrencyBalanceDto'
3026
3230
  }
3027
- },
3028
- total: {
3029
- type: 'number',
3030
- description: 'Total number of prices',
3031
- example: 42
3032
3231
  }
3033
3232
  },
3034
- required: ['items', 'total']
3233
+ required: ['date', 'byCurrency']
3035
3234
  } as const;
3036
3235
 
3037
- export const $UpdateBeanPriceDto = {
3236
+ export const $PortfolioTrendsResponseDto = {
3038
3237
  type: 'object',
3039
3238
  properties: {
3040
- currency: {
3041
- type: 'string',
3042
- description: 'Currency being priced'
3239
+ series: {
3240
+ description: 'Time series data points',
3241
+ type: 'array',
3242
+ items: {
3243
+ $ref: '#/components/schemas/TimeSeriesPointDto'
3244
+ }
3043
3245
  },
3044
- quoteCurrency: {
3246
+ summary: {
3247
+ description: 'Period summary',
3248
+ allOf: [
3249
+ {
3250
+ $ref: '#/components/schemas/TrendSummaryDto'
3251
+ }
3252
+ ]
3253
+ },
3254
+ period: {
3045
3255
  type: 'string',
3046
- description: 'Quote currency (pricing currency)'
3256
+ description: 'Period requested',
3257
+ example: '6m'
3047
3258
  },
3048
- amount: {
3049
- type: 'number',
3050
- description: 'Price amount (MUST be >= 0 per Beancount spec)',
3051
- minimum: 0
3259
+ granularity: {
3260
+ type: 'string',
3261
+ description: 'Data granularity',
3262
+ example: 'month'
3052
3263
  },
3053
- date: {
3264
+ currency: {
3054
3265
  type: 'string',
3055
- description: 'Price date (ISO 8601 format)'
3266
+ description: 'Base currency for converted values',
3267
+ example: 'CNY'
3056
3268
  },
3057
- metadata: {
3058
- type: 'object',
3059
- 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
+ }
3060
3283
  }
3061
- }
3284
+ },
3285
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3062
3286
  } as const;
3063
3287
 
3064
- export const $CreateRecurringRuleDto = {
3288
+ export const $CashFlowPointDto = {
3065
3289
  type: 'object',
3066
3290
  properties: {
3067
- name: {
3068
- type: 'string',
3069
- description: 'Rule name (unique per user)',
3070
- maxLength: 100
3071
- },
3072
- icon: {
3073
- type: 'string',
3074
- description: 'Icon emoji',
3075
- maxLength: 10
3076
- },
3077
- frequency: {
3291
+ month: {
3078
3292
  type: 'string',
3079
- description: 'Recurring frequency',
3080
- enum: [
3081
- 'WEEKLY',
3082
- 'BIWEEKLY',
3083
- 'MONTHLY',
3084
- 'BIMONTHLY',
3085
- 'QUARTERLY',
3086
- 'YEARLY',
3087
- 'CUSTOM'
3088
- ]
3089
- },
3090
- expectedAmount: {
3091
- type: 'number',
3092
- description: 'Expected amount (positive number)',
3093
- minimum: 0
3094
- },
3095
- expectedDay: {
3096
- type: 'number',
3097
- description: 'Expected day of month (1-31)',
3098
- minimum: 1,
3099
- maximum: 31
3100
- },
3101
- customIntervalDays: {
3102
- type: 'number',
3103
- description: 'Custom interval in days (required for CUSTOM frequency)',
3104
- minimum: 1
3293
+ description: 'Month key (YYYY-MM)',
3294
+ example: '2024-03'
3105
3295
  },
3106
- currency: {
3296
+ income: {
3107
3297
  type: 'string',
3108
- description: 'Currency code',
3109
- default: 'CNY',
3110
- maxLength: 10
3298
+ description: 'Income in base currency (absolute, converted)',
3299
+ example: '10000.00'
3111
3300
  },
3112
- matchPayeePattern: {
3301
+ expense: {
3113
3302
  type: 'string',
3114
- description: 'Payee matching pattern (supports wildcards)',
3115
- maxLength: 200
3116
- },
3117
- matchAmountTolerance: {
3118
- type: 'number',
3119
- description: 'Amount tolerance percentage (0-1)',
3120
- default: 0.075,
3121
- minimum: 0,
3122
- maximum: 1
3303
+ description: 'Expense in base currency (absolute, converted)',
3304
+ example: '5000.00'
3123
3305
  },
3124
- defaultExpenseAccount: {
3306
+ netSavings: {
3125
3307
  type: 'string',
3126
- description: 'Default expense account for auto-create',
3127
- maxLength: 200
3128
- },
3129
- defaultPaymentAccount: {
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: {
3130
3319
  type: 'string',
3131
- description: 'Default payment account for auto-create',
3132
- maxLength: 200
3320
+ description: 'Total income across the period',
3321
+ example: '60000.00'
3133
3322
  },
3134
- defaultPayee: {
3323
+ totalExpense: {
3135
3324
  type: 'string',
3136
- description: 'Default payee for auto-create',
3137
- maxLength: 200
3138
- },
3139
- autoCreate: {
3140
- type: 'boolean',
3141
- description: 'Auto-create transaction when expected date arrives',
3142
- default: false
3325
+ description: 'Total expense across the period',
3326
+ example: '30000.00'
3143
3327
  },
3144
- startDate: {
3328
+ totalNetSavings: {
3145
3329
  type: 'string',
3146
- description: 'Rule start date (ISO format)'
3330
+ description: 'income − expense across the period',
3331
+ example: '30000.00'
3147
3332
  },
3148
- endDate: {
3333
+ averageMonthlyNetSavings: {
3149
3334
  type: 'string',
3150
- description: 'Rule end date (ISO format)'
3335
+ description:
3336
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3337
+ example: '5000.00'
3151
3338
  }
3152
3339
  },
3153
3340
  required: [
3154
- 'name',
3155
- 'frequency',
3156
- 'expectedAmount',
3157
- 'currency',
3158
- 'matchAmountTolerance',
3159
- 'autoCreate'
3341
+ 'totalIncome',
3342
+ 'totalExpense',
3343
+ 'totalNetSavings',
3344
+ 'averageMonthlyNetSavings'
3160
3345
  ]
3161
3346
  } as const;
3162
3347
 
3163
- export const $RecurringRuleResponseDto = {
3348
+ export const $CashFlowTrendsResponseDto = {
3164
3349
  type: 'object',
3165
3350
  properties: {
3166
- id: {
3167
- type: 'string',
3168
- 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
+ }
3169
3358
  },
3170
- userId: {
3171
- type: 'string',
3172
- description: 'User ID'
3359
+ summary: {
3360
+ description: 'Period totals',
3361
+ allOf: [
3362
+ {
3363
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
3364
+ }
3365
+ ]
3173
3366
  },
3174
- name: {
3367
+ period: {
3175
3368
  type: 'string',
3176
- description: 'Rule name'
3177
- },
3178
- icon: {
3179
- type: 'object',
3180
- description: 'Icon emoji'
3369
+ description: 'Period requested',
3370
+ example: '6m'
3181
3371
  },
3182
- frequency: {
3372
+ granularity: {
3183
3373
  type: 'string',
3184
- description: 'Recurring frequency'
3185
- },
3186
- expectedAmount: {
3187
- type: 'number',
3188
- description: 'Expected amount'
3189
- },
3190
- expectedDay: {
3191
- type: 'object',
3192
- description: 'Expected day of month'
3374
+ description: 'Data granularity (v1 returns month buckets)',
3375
+ example: 'month'
3193
3376
  },
3194
- customIntervalDays: {
3195
- type: 'object',
3196
- description: 'Custom interval in days'
3377
+ currency: {
3378
+ type: 'string',
3379
+ description: 'Base currency for converted values',
3380
+ example: 'CNY'
3197
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: {
3198
3416
  currency: {
3199
3417
  type: 'string',
3200
- description: 'Currency code'
3418
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3419
+ example: 'USD'
3201
3420
  },
3202
- matchPayeePattern: {
3203
- type: 'object',
3204
- description: 'Payee matching pattern'
3421
+ quoteCurrency: {
3422
+ type: 'string',
3423
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3424
+ example: 'CNY'
3205
3425
  },
3206
- matchAmountTolerance: {
3426
+ amount: {
3207
3427
  type: 'number',
3208
- 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
3209
3432
  },
3210
- defaultExpenseAccount: {
3211
- type: 'object',
3212
- description: 'Default expense account'
3433
+ date: {
3434
+ type: 'string',
3435
+ description: 'Price date (ISO 8601 format)',
3436
+ example: '2024-11-05'
3213
3437
  },
3214
- defaultPaymentAccount: {
3438
+ metadata: {
3215
3439
  type: 'object',
3216
- 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'
3217
3459
  },
3218
- defaultPayee: {
3219
- type: 'object',
3220
- description: 'Default payee'
3460
+ userId: {
3461
+ type: 'string',
3462
+ description: 'User ID (owner of the price)',
3463
+ example: 'user-123'
3221
3464
  },
3222
- isActive: {
3223
- type: 'boolean',
3224
- description: 'Whether rule is active'
3465
+ currency: {
3466
+ type: 'string',
3467
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3468
+ example: 'BTC'
3225
3469
  },
3226
- startDate: {
3470
+ quoteCurrency: {
3227
3471
  type: 'string',
3228
- description: 'Rule start date (YYYY-MM-DD)'
3472
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
3473
+ example: 'USD'
3229
3474
  },
3230
- endDate: {
3231
- type: 'object',
3232
- 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
3233
3480
  },
3234
- autoCreate: {
3235
- type: 'boolean',
3236
- 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'
3237
3487
  },
3238
- lastOccurrence: {
3488
+ meta: {
3239
3489
  type: 'object',
3240
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3241
- },
3242
- totalCount: {
3243
- type: 'number',
3244
- 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
+ }
3245
3497
  },
3246
3498
  createdAt: {
3247
3499
  format: 'date-time',
3248
3500
  type: 'string',
3249
- description: 'Created at timestamp'
3501
+ description: 'Creation timestamp',
3502
+ example: '2024-11-03T10:00:00Z'
3250
3503
  },
3251
3504
  updatedAt: {
3252
3505
  format: 'date-time',
3253
3506
  type: 'string',
3254
- description: 'Updated at timestamp'
3507
+ description: 'Last update timestamp',
3508
+ example: '2024-11-03T10:00:00Z'
3255
3509
  }
3256
3510
  },
3257
3511
  required: [
3258
3512
  'id',
3259
3513
  'userId',
3260
- 'name',
3261
- 'frequency',
3262
- 'expectedAmount',
3263
3514
  'currency',
3264
- 'matchAmountTolerance',
3265
- 'isActive',
3266
- 'startDate',
3267
- 'autoCreate',
3268
- 'totalCount',
3515
+ 'quoteCurrency',
3516
+ 'amount',
3517
+ 'date',
3518
+ 'meta',
3269
3519
  'createdAt',
3270
3520
  'updatedAt'
3271
3521
  ]
3272
3522
  } as const;
3273
3523
 
3274
- export const $CreateRuleFromTransactionDto = {
3524
+ export const $PriceListResponseDto = {
3275
3525
  type: 'object',
3276
3526
  properties: {
3277
- frequency: {
3278
- type: 'string',
3279
- description: 'Recurring frequency',
3280
- enum: [
3281
- 'WEEKLY',
3282
- 'BIWEEKLY',
3283
- 'MONTHLY',
3284
- 'BIMONTHLY',
3285
- 'QUARTERLY',
3286
- 'YEARLY',
3287
- 'CUSTOM'
3288
- ],
3289
- example: 'MONTHLY'
3290
- },
3291
- name: {
3292
- type: 'string',
3293
- description: 'Optional name override (default: transaction payee)',
3294
- maxLength: 100
3527
+ items: {
3528
+ description: 'List of prices',
3529
+ type: 'array',
3530
+ items: {
3531
+ $ref: '#/components/schemas/PriceResponseDto'
3532
+ }
3295
3533
  },
3296
- icon: {
3297
- type: 'string',
3298
- description: 'Optional icon emoji',
3299
- maxLength: 10
3534
+ total: {
3535
+ type: 'number',
3536
+ description: 'Total number of prices',
3537
+ example: 42
3300
3538
  }
3301
3539
  },
3302
- required: ['frequency']
3540
+ required: ['items', 'total']
3303
3541
  } as const;
3304
3542
 
3305
- export const $RecurringRuleWithStatsResponseDto = {
3543
+ export const $UpdateBeanPriceDto = {
3306
3544
  type: 'object',
3307
3545
  properties: {
3308
- id: {
3309
- type: 'string',
3310
- description: 'Rule ID'
3311
- },
3312
- userId: {
3546
+ currency: {
3313
3547
  type: 'string',
3314
- description: 'User ID'
3548
+ description: 'Currency being priced'
3315
3549
  },
3316
- name: {
3550
+ quoteCurrency: {
3317
3551
  type: 'string',
3318
- description: 'Rule name'
3552
+ description: 'Quote currency (pricing currency)'
3319
3553
  },
3320
- icon: {
3321
- type: 'object',
3322
- description: 'Icon emoji'
3554
+ amount: {
3555
+ type: 'number',
3556
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
3557
+ minimum: 0
3323
3558
  },
3324
- frequency: {
3559
+ date: {
3325
3560
  type: 'string',
3326
- description: 'Recurring frequency'
3327
- },
3328
- expectedAmount: {
3329
- type: 'number',
3330
- description: 'Expected amount'
3561
+ description: 'Price date (ISO 8601 format)'
3331
3562
  },
3332
- expectedDay: {
3563
+ metadata: {
3333
3564
  type: 'object',
3334
- description: 'Expected day of month'
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'
3577
+ }
3578
+ },
3579
+ required: ['accessToken']
3580
+ } as const;
3581
+
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 = {
3597
+ type: 'object',
3598
+ properties: {
3599
+ id: {
3600
+ type: 'string',
3601
+ description: 'User ID'
3335
3602
  },
3336
- customIntervalDays: {
3337
- type: 'object',
3338
- description: 'Custom interval in days'
3603
+ role: {
3604
+ type: 'string',
3605
+ description: 'Assigned user role'
3339
3606
  },
3340
- currency: {
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: {
3341
3630
  type: 'string',
3342
- description: 'Currency code'
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...'
3343
3645
  },
3344
- matchPayeePattern: {
3345
- type: 'object',
3346
- description: 'Payee matching pattern'
3646
+ accessToken: {
3647
+ type: 'string',
3648
+ description: 'Auto-generated access token'
3347
3649
  },
3348
- matchAmountTolerance: {
3650
+ role: {
3651
+ type: 'string',
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: {
3349
3663
  type: 'number',
3350
- description: 'Amount tolerance percentage'
3664
+ description: 'Security ID'
3351
3665
  },
3352
- defaultExpenseAccount: {
3353
- type: 'object',
3354
- description: 'Default expense account'
3666
+ annualInterestRate: {
3667
+ type: 'number',
3668
+ description: 'Annual interest rate',
3669
+ example: 0.05
3355
3670
  },
3356
- defaultPaymentAccount: {
3357
- type: 'object',
3358
- description: 'Default payment account'
3671
+ currency: {
3672
+ type: 'string',
3673
+ description: 'Currency code',
3674
+ example: 'USD'
3359
3675
  },
3360
- defaultPayee: {
3361
- type: 'object',
3362
- description: 'Default payee'
3676
+ baseCurrency: {
3677
+ type: 'string',
3678
+ description: 'Base currency code',
3679
+ example: 'USD'
3363
3680
  },
3364
- isActive: {
3365
- type: 'boolean',
3366
- description: 'Whether rule is active'
3681
+ benchmark: {
3682
+ type: 'string',
3683
+ description: 'Benchmark symbol',
3684
+ example: 'SPY'
3367
3685
  },
3368
- startDate: {
3686
+ colorScheme: {
3369
3687
  type: 'string',
3370
- description: 'Rule start date (YYYY-MM-DD)'
3688
+ description: 'Color scheme',
3689
+ enum: ['DARK', 'LIGHT']
3371
3690
  },
3372
- endDate: {
3373
- type: 'object',
3374
- description: 'Rule end date (YYYY-MM-DD)'
3691
+ dateRange: {
3692
+ type: 'string',
3693
+ description: 'Date range filter',
3694
+ example: '1y'
3375
3695
  },
3376
- autoCreate: {
3377
- type: 'boolean',
3378
- description: 'Auto-create transaction on expected date'
3696
+ emergencyFund: {
3697
+ type: 'number',
3698
+ description: 'Emergency fund amount',
3699
+ example: 10000
3379
3700
  },
3380
- lastOccurrence: {
3381
- type: 'object',
3382
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3701
+ 'filters.accounts': {
3702
+ description: 'Account filter IDs',
3703
+ type: 'array',
3704
+ items: {
3705
+ type: 'string'
3706
+ }
3383
3707
  },
3384
- totalCount: {
3385
- type: 'number',
3386
- description: 'Total matched transactions count'
3708
+ 'filters.assetClasses': {
3709
+ description: 'Asset class filters',
3710
+ type: 'array',
3711
+ items: {
3712
+ type: 'string'
3713
+ }
3387
3714
  },
3388
- createdAt: {
3389
- format: 'date-time',
3715
+ 'filters.dataSource': {
3390
3716
  type: 'string',
3391
- description: 'Created at timestamp'
3717
+ description: 'Data source filter'
3392
3718
  },
3393
- updatedAt: {
3394
- format: 'date-time',
3719
+ 'filters.symbol': {
3395
3720
  type: 'string',
3396
- description: 'Updated at timestamp'
3721
+ description: 'Symbol filter'
3397
3722
  },
3398
- pendingCount: {
3399
- type: 'number',
3400
- description: 'Number of pending expected transactions'
3723
+ 'filters.tags': {
3724
+ description: 'Tag filters',
3725
+ type: 'array',
3726
+ items: {
3727
+ type: 'string'
3728
+ }
3401
3729
  },
3402
- overdueCount: {
3403
- type: 'number',
3404
- description: 'Number of overdue expected transactions'
3730
+ isExperimentalFeatures: {
3731
+ type: 'boolean',
3732
+ description: 'Enable experimental features'
3405
3733
  },
3406
- nextExpectedDate: {
3407
- type: 'object',
3408
- description: 'Next expected date (YYYY-MM-DD)'
3734
+ isRestrictedView: {
3735
+ type: 'boolean',
3736
+ description: 'Enable restricted view mode'
3409
3737
  },
3410
- totalAmount: {
3411
- type: 'number',
3412
- description: 'Total amount of all matched transactions'
3738
+ language: {
3739
+ type: 'string',
3740
+ description: 'Language code',
3741
+ example: 'en'
3413
3742
  },
3414
- averageAmount: {
3415
- type: 'number',
3416
- description: 'Average amount per transaction'
3743
+ locale: {
3744
+ type: 'string',
3745
+ description: 'Locale code',
3746
+ example: 'en-US'
3417
3747
  },
3418
- transactionCount: {
3748
+ projectedTotalAmount: {
3419
3749
  type: 'number',
3420
- description: 'Number of matched transactions'
3421
- },
3422
- firstDate: {
3423
- type: 'object',
3424
- description: 'First matched transaction date (YYYY-MM-DD)'
3750
+ description: 'Projected total amount',
3751
+ example: 1000000
3425
3752
  },
3426
- lastDate: {
3427
- type: 'object',
3428
- description: 'Last matched transaction date (YYYY-MM-DD)'
3753
+ retirementDate: {
3754
+ type: 'string',
3755
+ description: 'Retirement date in ISO 8601 format',
3756
+ example: '2050-01-01'
3429
3757
  },
3430
- variance: {
3758
+ savingsRate: {
3431
3759
  type: 'number',
3432
- description: 'Amount variance (standard deviation squared)'
3760
+ description: 'Savings rate percentage',
3761
+ example: 0.2
3433
3762
  },
3434
- upcomingCount: {
3435
- type: 'number',
3436
- description: 'Number of upcoming expected transactions'
3763
+ viewMode: {
3764
+ type: 'string',
3765
+ description: 'View mode',
3766
+ enum: ['DEFAULT', 'ZEN']
3437
3767
  }
3438
- },
3439
- required: [
3440
- 'id',
3441
- 'userId',
3442
- 'name',
3443
- 'frequency',
3444
- 'expectedAmount',
3445
- 'currency',
3446
- 'matchAmountTolerance',
3447
- 'isActive',
3448
- 'startDate',
3449
- 'autoCreate',
3450
- 'totalCount',
3451
- 'createdAt',
3452
- 'updatedAt',
3453
- 'pendingCount',
3454
- 'overdueCount',
3455
- 'totalAmount',
3456
- 'averageAmount',
3457
- 'transactionCount',
3458
- 'variance',
3459
- 'upcomingCount'
3460
- ]
3768
+ }
3461
3769
  } as const;
3462
3770
 
3463
- export const $UpdateRecurringRuleDto = {
3771
+ export const $UpdatePropertyDto = {
3772
+ type: 'object',
3773
+ properties: {
3774
+ value: {
3775
+ type: 'string',
3776
+ description: 'Property value'
3777
+ }
3778
+ },
3779
+ required: ['value']
3780
+ } as const;
3781
+
3782
+ export const $CreateRecurringRuleDto = {
3464
3783
  type: 'object',
3465
3784
  properties: {
3466
3785
  name: {
3467
3786
  type: 'string',
3468
- description: 'Rule name',
3787
+ description: 'Rule name (unique per user)',
3469
3788
  maxLength: 100
3470
3789
  },
3471
3790
  icon: {
@@ -3488,7 +3807,7 @@ export const $UpdateRecurringRuleDto = {
3488
3807
  },
3489
3808
  expectedAmount: {
3490
3809
  type: 'number',
3491
- description: 'Expected amount',
3810
+ description: 'Expected amount (positive number)',
3492
3811
  minimum: 0
3493
3812
  },
3494
3813
  expectedDay: {
@@ -3499,7 +3818,7 @@ export const $UpdateRecurringRuleDto = {
3499
3818
  },
3500
3819
  customIntervalDays: {
3501
3820
  type: 'number',
3502
- description: 'Custom interval in days',
3821
+ description: 'Custom interval in days (required for CUSTOM frequency)',
3503
3822
  minimum: 1
3504
3823
  },
3505
3824
  currency: {
@@ -3509,118 +3828,136 @@ export const $UpdateRecurringRuleDto = {
3509
3828
  },
3510
3829
  matchPayeePattern: {
3511
3830
  type: 'string',
3512
- description: 'Payee matching pattern',
3831
+ description: 'Payee matching pattern (supports wildcards)',
3513
3832
  maxLength: 200
3514
3833
  },
3515
3834
  matchAmountTolerance: {
3516
3835
  type: 'number',
3517
3836
  description: 'Amount tolerance percentage (0-1)',
3837
+ default: 0.075,
3518
3838
  minimum: 0,
3519
3839
  maximum: 1
3520
3840
  },
3521
3841
  defaultExpenseAccount: {
3522
3842
  type: 'string',
3523
- description: 'Default expense account',
3843
+ description: 'Default expense account for auto-create',
3524
3844
  maxLength: 200
3525
3845
  },
3526
3846
  defaultPaymentAccount: {
3527
3847
  type: 'string',
3528
- description: 'Default payment account',
3848
+ description: 'Default payment account for auto-create',
3529
3849
  maxLength: 200
3530
3850
  },
3531
3851
  defaultPayee: {
3532
3852
  type: 'string',
3533
- description: 'Default payee',
3853
+ description: 'Default payee for auto-create',
3534
3854
  maxLength: 200
3535
3855
  },
3536
3856
  autoCreate: {
3537
3857
  type: 'boolean',
3538
- description: 'Auto-create transaction'
3858
+ description: 'Auto-create transaction when expected date arrives',
3859
+ default: false
3539
3860
  },
3540
- isActive: {
3541
- type: 'boolean',
3542
- description: 'Rule active status'
3861
+ startDate: {
3862
+ type: 'string',
3863
+ description: 'Rule start date (ISO format)'
3543
3864
  },
3544
3865
  endDate: {
3545
3866
  type: 'string',
3546
3867
  description: 'Rule end date (ISO format)'
3547
3868
  }
3548
- }
3869
+ },
3870
+ required: [
3871
+ 'name',
3872
+ 'frequency',
3873
+ 'expectedAmount',
3874
+ 'matchAmountTolerance',
3875
+ 'autoCreate'
3876
+ ]
3549
3877
  } as const;
3550
3878
 
3551
- export const $ExpectedTransactionRuleDto = {
3879
+ export const $RecurringRuleResponseDto = {
3552
3880
  type: 'object',
3553
3881
  properties: {
3882
+ id: {
3883
+ type: 'string',
3884
+ description: 'Rule ID'
3885
+ },
3886
+ userId: {
3887
+ type: 'string',
3888
+ description: 'User ID'
3889
+ },
3554
3890
  name: {
3555
3891
  type: 'string',
3556
3892
  description: 'Rule name'
3557
3893
  },
3558
3894
  icon: {
3559
- type: 'object',
3560
- description: 'Rule icon'
3895
+ type: 'string',
3896
+ description: 'Icon emoji'
3561
3897
  },
3562
3898
  frequency: {
3563
3899
  type: 'string',
3564
- 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'
3565
3913
  },
3566
3914
  currency: {
3567
3915
  type: 'string',
3568
3916
  description: 'Currency code'
3569
- }
3570
- },
3571
- required: ['name', 'frequency', 'currency']
3572
- } as const;
3573
-
3574
- export const $ExpectedTransactionResponseDto = {
3575
- type: 'object',
3576
- properties: {
3577
- id: {
3578
- type: 'string',
3579
- description: 'Expected transaction ID'
3580
3917
  },
3581
- userId: {
3918
+ matchPayeePattern: {
3582
3919
  type: 'string',
3583
- description: 'User ID'
3920
+ description: 'Payee matching pattern'
3584
3921
  },
3585
- ruleId: {
3586
- type: 'string',
3587
- description: 'Associated rule ID'
3922
+ matchAmountTolerance: {
3923
+ type: 'number',
3924
+ description: 'Amount tolerance percentage'
3588
3925
  },
3589
- expectedDate: {
3926
+ defaultExpenseAccount: {
3590
3927
  type: 'string',
3591
- description: 'Expected date (YYYY-MM-DD)'
3928
+ description: 'Default expense account'
3592
3929
  },
3593
- expectedAmount: {
3594
- type: 'number',
3595
- description: 'Expected amount'
3930
+ defaultPaymentAccount: {
3931
+ type: 'string',
3932
+ description: 'Default payment account'
3596
3933
  },
3597
- status: {
3934
+ defaultPayee: {
3598
3935
  type: 'string',
3599
- description: 'Status (PENDING, COMPLETED, SKIPPED)'
3936
+ description: 'Default payee'
3600
3937
  },
3601
- matchedTransactionId: {
3602
- type: 'object',
3603
- description: 'Matched transaction ID'
3938
+ isActive: {
3939
+ type: 'boolean',
3940
+ description: 'Whether rule is active'
3604
3941
  },
3605
- matchedAt: {
3606
- type: 'object',
3607
- description: 'Match timestamp (ISO 8601)'
3942
+ startDate: {
3943
+ type: 'string',
3944
+ description: 'Rule start date (YYYY-MM-DD)'
3608
3945
  },
3609
- matchConfidence: {
3610
- type: 'object',
3611
- description: 'Match confidence score (0-1)'
3946
+ endDate: {
3947
+ type: 'string',
3948
+ description: 'Rule end date (YYYY-MM-DD)'
3612
3949
  },
3613
- isOverdue: {
3950
+ autoCreate: {
3614
3951
  type: 'boolean',
3615
- description: 'Whether this expected transaction is overdue'
3952
+ description: 'Auto-create transaction on expected date'
3616
3953
  },
3617
- rule: {
3618
- description: 'Rule information',
3619
- allOf: [
3620
- {
3621
- $ref: '#/components/schemas/ExpectedTransactionRuleDto'
3622
- }
3623
- ]
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'
3624
3961
  },
3625
3962
  createdAt: {
3626
3963
  format: 'date-time',
@@ -3636,647 +3973,581 @@ export const $ExpectedTransactionResponseDto = {
3636
3973
  required: [
3637
3974
  'id',
3638
3975
  'userId',
3639
- 'ruleId',
3640
- 'expectedDate',
3976
+ 'name',
3977
+ 'frequency',
3641
3978
  'expectedAmount',
3642
- 'status',
3643
- 'isOverdue',
3644
- 'rule',
3979
+ 'currency',
3980
+ 'matchAmountTolerance',
3981
+ 'isActive',
3982
+ 'startDate',
3983
+ 'autoCreate',
3984
+ 'totalCount',
3645
3985
  'createdAt',
3646
3986
  'updatedAt'
3647
3987
  ]
3648
3988
  } as const;
3649
3989
 
3650
- export const $ExpectedTransactionListResponseDto = {
3990
+ export const $CreateRuleFromTransactionDto = {
3651
3991
  type: 'object',
3652
3992
  properties: {
3653
- items: {
3654
- type: 'array',
3655
- items: {
3656
- $ref: '#/components/schemas/ExpectedTransactionResponseDto'
3657
- }
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'
3658
4006
  },
3659
- total: {
3660
- type: 'number',
3661
- description: 'Total count'
3662
- }
3663
- },
3664
- required: ['items', 'total']
3665
- } as const;
3666
-
3667
- export const $ConfirmMatchDto = {
3668
- type: 'object',
3669
- properties: {
3670
- transactionId: {
4007
+ name: {
3671
4008
  type: 'string',
3672
- 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
3673
4016
  }
3674
4017
  },
3675
- required: ['transactionId']
4018
+ required: ['frequency']
3676
4019
  } as const;
3677
4020
 
3678
- export const $EnterNowDto = {
4021
+ export const $RecurringRuleWithStatsResponseDto = {
3679
4022
  type: 'object',
3680
4023
  properties: {
3681
- expenseAccount: {
4024
+ id: {
3682
4025
  type: 'string',
3683
- description:
3684
- 'Override expense account (uses rule default if not provided)',
3685
- maxLength: 200
4026
+ description: 'Rule ID'
3686
4027
  },
3687
- paymentAccount: {
4028
+ userId: {
3688
4029
  type: 'string',
3689
- description:
3690
- 'Override payment account (uses rule default if not provided)',
3691
- maxLength: 200
3692
- },
3693
- amount: {
3694
- type: 'number',
3695
- description: 'Override amount (uses expected amount if not provided)',
3696
- minimum: 0
4030
+ description: 'User ID'
3697
4031
  },
3698
- payee: {
4032
+ name: {
3699
4033
  type: 'string',
3700
- description: 'Override payee (uses rule default if not provided)',
3701
- maxLength: 200
4034
+ description: 'Rule name'
3702
4035
  },
3703
- narration: {
3704
- type: 'string',
3705
- description: 'Optional narration',
3706
- maxLength: 500
3707
- }
3708
- }
3709
- } as const;
3710
-
3711
- export const $ForecastItemDto = {
3712
- type: 'object',
3713
- properties: {
3714
- rule: {
4036
+ icon: {
3715
4037
  type: 'string',
3716
- description: 'Rule name',
3717
- example: 'Rent'
4038
+ description: 'Icon emoji'
3718
4039
  },
3719
- ruleId: {
4040
+ frequency: {
3720
4041
  type: 'string',
3721
- description: 'Rule ID',
3722
- example: 'clx123...'
4042
+ description: 'Recurring frequency'
3723
4043
  },
3724
- amount: {
4044
+ expectedAmount: {
3725
4045
  type: 'number',
3726
- description: 'Expected amount',
3727
- example: 3000
4046
+ description: 'Expected amount'
3728
4047
  },
3729
- date: {
3730
- type: 'string',
3731
- description: 'Expected date (YYYY-MM-DD)',
3732
- example: '2024-04-01'
4048
+ expectedDay: {
4049
+ type: 'number',
4050
+ description: 'Expected day of month'
3733
4051
  },
3734
- icon: {
3735
- type: 'string',
3736
- description: 'Rule icon emoji',
3737
- example: '🏠',
3738
- nullable: true
4052
+ customIntervalDays: {
4053
+ type: 'number',
4054
+ description: 'Custom interval in days'
3739
4055
  },
3740
4056
  currency: {
3741
4057
  type: 'string',
3742
- description: 'Currency code',
3743
- example: 'CNY'
3744
- }
3745
- },
3746
- required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
3747
- } as const;
3748
-
3749
- export const $MonthlyForecastDto = {
3750
- type: 'object',
3751
- properties: {
3752
- month: {
3753
- type: 'string',
3754
- description: 'Month (YYYY-MM)',
3755
- example: '2024-04'
4058
+ description: 'Currency code'
3756
4059
  },
3757
- expectedOutflow: {
3758
- type: 'number',
3759
- description: 'Total expected outflow for the month',
3760
- example: 8500
4060
+ matchPayeePattern: {
4061
+ type: 'string',
4062
+ description: 'Payee matching pattern'
3761
4063
  },
3762
- itemCount: {
4064
+ matchAmountTolerance: {
3763
4065
  type: 'number',
3764
- description: 'Number of expected transactions',
3765
- example: 3
3766
- },
3767
- byCurrency: {
3768
- type: 'object',
3769
- description: 'Breakdown by currency',
3770
- example: {
3771
- CNY: 8500,
3772
- USD: 100
3773
- }
4066
+ description: 'Amount tolerance percentage'
3774
4067
  },
3775
- items: {
3776
- description: 'Individual forecast items',
3777
- type: 'array',
3778
- items: {
3779
- $ref: '#/components/schemas/ForecastItemDto'
3780
- }
3781
- }
3782
- },
3783
- required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
3784
- } as const;
3785
-
3786
- export const $ForecastResponseDto = {
3787
- type: 'object',
3788
- properties: {
3789
- forecast: {
3790
- description: 'Monthly forecast data',
3791
- type: 'array',
3792
- items: {
3793
- $ref: '#/components/schemas/MonthlyForecastDto'
3794
- }
4068
+ defaultExpenseAccount: {
4069
+ type: 'string',
4070
+ description: 'Default expense account'
3795
4071
  },
3796
- totalOutflow: {
3797
- type: 'number',
3798
- description: 'Total expected outflow across all months',
3799
- example: 25500
4072
+ defaultPaymentAccount: {
4073
+ type: 'string',
4074
+ description: 'Default payment account'
3800
4075
  },
3801
- totalByCurrency: {
3802
- type: 'object',
3803
- description: 'Total by currency across all months',
3804
- example: {
3805
- CNY: 25500,
3806
- USD: 300
3807
- }
4076
+ defaultPayee: {
4077
+ type: 'string',
4078
+ description: 'Default payee'
3808
4079
  },
3809
- rulesCount: {
3810
- type: 'number',
3811
- description: 'Number of active recurring rules included',
3812
- example: 5
4080
+ isActive: {
4081
+ type: 'boolean',
4082
+ description: 'Whether rule is active'
3813
4083
  },
3814
- periodStart: {
4084
+ startDate: {
3815
4085
  type: 'string',
3816
- description: 'Forecast period start date',
3817
- example: '2024-04-01'
4086
+ description: 'Rule start date (YYYY-MM-DD)'
3818
4087
  },
3819
- periodEnd: {
4088
+ endDate: {
3820
4089
  type: 'string',
3821
- description: 'Forecast period end date',
3822
- example: '2024-06-30'
3823
- }
3824
- },
3825
- required: [
3826
- 'forecast',
3827
- 'totalOutflow',
3828
- 'totalByCurrency',
3829
- 'rulesCount',
3830
- 'periodStart',
3831
- 'periodEnd'
3832
- ]
3833
- } as const;
3834
-
3835
- export const $CurrencyBalanceDto = {
3836
- type: 'object',
3837
- properties: {
3838
- currency: {
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: {
3839
4097
  type: 'string',
3840
- description: 'ISO 4217 currency code',
3841
- example: 'CNY'
4098
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
3842
4099
  },
3843
- balance: {
4100
+ totalCount: {
4101
+ type: 'number',
4102
+ description: 'Total matched transactions count'
4103
+ },
4104
+ createdAt: {
4105
+ format: 'date-time',
3844
4106
  type: 'string',
3845
- description: 'Balance amount',
3846
- example: '500000.00'
3847
- }
3848
- },
3849
- required: ['currency', 'balance']
3850
- } as const;
3851
-
3852
- export const $TimeSeriesPointDto = {
3853
- type: 'object',
3854
- properties: {
3855
- date: {
4107
+ description: 'Created at timestamp'
4108
+ },
4109
+ updatedAt: {
4110
+ format: 'date-time',
3856
4111
  type: 'string',
3857
- description: 'Date in YYYY-MM-DD format',
3858
- example: '2024-06-15'
4112
+ description: 'Updated at timestamp'
3859
4113
  },
3860
- value: {
4114
+ pendingCount: {
4115
+ type: 'number',
4116
+ description: 'Number of pending expected transactions'
4117
+ },
4118
+ overdueCount: {
4119
+ type: 'number',
4120
+ description: 'Number of overdue expected transactions'
4121
+ },
4122
+ nextExpectedDate: {
3861
4123
  type: 'string',
3862
- description: 'Value at this date (in base currency)',
3863
- example: '500000.00'
4124
+ description: 'Next expected date (YYYY-MM-DD)'
3864
4125
  },
3865
- change: {
3866
- type: 'object',
3867
- description: 'Change from previous point',
3868
- example: '5000.00'
4126
+ totalAmount: {
4127
+ type: 'number',
4128
+ description: 'Total amount of all matched transactions'
3869
4129
  },
3870
- assets: {
4130
+ averageAmount: {
4131
+ type: 'number',
4132
+ description: 'Average amount per transaction'
4133
+ },
4134
+ transactionCount: {
4135
+ type: 'number',
4136
+ description: 'Number of matched transactions'
4137
+ },
4138
+ firstDate: {
3871
4139
  type: 'string',
3872
- description: 'Total assets at this date (in base currency)',
3873
- example: '494338.00'
4140
+ description: 'First matched transaction date (YYYY-MM-DD)'
3874
4141
  },
3875
- liabilities: {
4142
+ lastDate: {
3876
4143
  type: 'string',
3877
- description: 'Total liabilities at this date (in base currency)',
3878
- example: '310098.00'
4144
+ description: 'Last matched transaction date (YYYY-MM-DD)'
3879
4145
  },
3880
- byCurrency: {
3881
- description: 'Multi-currency breakdown for this point',
3882
- type: 'array',
3883
- items: {
3884
- $ref: '#/components/schemas/CurrencyBalanceDto'
3885
- }
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'
3886
4153
  }
3887
4154
  },
3888
- required: ['date', 'value']
4155
+ required: [
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'
4176
+ ]
3889
4177
  } as const;
3890
4178
 
3891
- export const $TrendSummaryDto = {
4179
+ export const $UpdateRecurringRuleDto = {
3892
4180
  type: 'object',
3893
4181
  properties: {
3894
- startValue: {
4182
+ name: {
3895
4183
  type: 'string',
3896
- description: 'Value at start of period',
3897
- example: '450000.00'
4184
+ description: 'Rule name (unique per user)',
4185
+ maxLength: 100
3898
4186
  },
3899
- endValue: {
4187
+ icon: {
3900
4188
  type: 'string',
3901
- description: 'Value at end of period',
3902
- example: '500000.00'
4189
+ description: 'Icon emoji',
4190
+ maxLength: 10
3903
4191
  },
3904
- totalChange: {
4192
+ frequency: {
3905
4193
  type: 'string',
3906
- description: 'Total change over period',
3907
- example: '50000.00'
4194
+ description: 'Recurring frequency',
4195
+ enum: [
4196
+ 'WEEKLY',
4197
+ 'BIWEEKLY',
4198
+ 'MONTHLY',
4199
+ 'BIMONTHLY',
4200
+ 'QUARTERLY',
4201
+ 'YEARLY',
4202
+ 'CUSTOM'
4203
+ ]
3908
4204
  },
3909
- totalChangePercentage: {
3910
- type: 'string',
3911
- description: 'Total change percentage',
3912
- example: '+11.11%'
3913
- }
3914
- },
3915
- required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
3916
- } as const;
3917
-
3918
- export const $MultiCurrencyPointDto = {
3919
- type: 'object',
3920
- properties: {
3921
- date: {
4205
+ expectedAmount: {
4206
+ type: 'number',
4207
+ description: 'Expected amount (positive number)',
4208
+ minimum: 0
4209
+ },
4210
+ expectedDay: {
4211
+ type: 'number',
4212
+ description: 'Expected day of month (1-31)',
4213
+ minimum: 1,
4214
+ maximum: 31
4215
+ },
4216
+ currency: {
3922
4217
  type: 'string',
3923
- description: 'Date in YYYY-MM-DD format',
3924
- example: '2024-06-15'
4218
+ description: 'Currency code',
4219
+ maxLength: 10
3925
4220
  },
3926
- byCurrency: {
3927
- description: 'Balances by currency',
3928
- type: 'array',
3929
- items: {
3930
- $ref: '#/components/schemas/CurrencyBalanceDto'
3931
- }
3932
- }
3933
- },
3934
- required: ['date', 'byCurrency']
3935
- } as const;
3936
-
3937
- export const $PortfolioTrendsResponseDto = {
3938
- type: 'object',
3939
- properties: {
3940
- series: {
3941
- description: 'Time series data points',
3942
- type: 'array',
3943
- items: {
3944
- $ref: '#/components/schemas/TimeSeriesPointDto'
3945
- }
4221
+ matchPayeePattern: {
4222
+ type: 'string',
4223
+ description: 'Payee matching pattern (supports wildcards)',
4224
+ maxLength: 200
3946
4225
  },
3947
- summary: {
3948
- description: 'Period summary',
3949
- allOf: [
3950
- {
3951
- $ref: '#/components/schemas/TrendSummaryDto'
3952
- }
3953
- ]
4226
+ matchAmountTolerance: {
4227
+ type: 'number',
4228
+ description: 'Amount tolerance percentage (0-1)',
4229
+ default: 0.075,
4230
+ minimum: 0,
4231
+ maximum: 1
3954
4232
  },
3955
- period: {
4233
+ defaultExpenseAccount: {
3956
4234
  type: 'string',
3957
- description: 'Period requested',
3958
- example: '6m'
4235
+ description: 'Default expense account for auto-create',
4236
+ maxLength: 200
3959
4237
  },
3960
- granularity: {
4238
+ defaultPaymentAccount: {
3961
4239
  type: 'string',
3962
- description: 'Data granularity',
3963
- example: 'month'
4240
+ description: 'Default payment account for auto-create',
4241
+ maxLength: 200
3964
4242
  },
3965
- currency: {
4243
+ defaultPayee: {
3966
4244
  type: 'string',
3967
- description: 'Base currency for converted values',
3968
- example: 'CNY'
4245
+ description: 'Default payee for auto-create',
4246
+ maxLength: 200
3969
4247
  },
3970
- byCurrency: {
3971
- description:
3972
- 'Multi-currency time series (each point has currency breakdown)',
3973
- type: 'array',
3974
- items: {
3975
- $ref: '#/components/schemas/MultiCurrencyPointDto'
3976
- }
4248
+ autoCreate: {
4249
+ type: 'boolean',
4250
+ description: 'Auto-create transaction when expected date arrives',
4251
+ default: false
3977
4252
  },
3978
- warnings: {
3979
- description: 'Exchange rate warnings',
3980
- type: 'array',
3981
- items: {
3982
- $ref: '#/components/schemas/ExchangeRateWarningDto'
3983
- }
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'
3984
4265
  }
3985
- },
3986
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4266
+ }
3987
4267
  } as const;
3988
4268
 
3989
- export const $CashFlowPointDto = {
4269
+ export const $ExpectedTransactionRuleDto = {
3990
4270
  type: 'object',
3991
4271
  properties: {
3992
- month: {
4272
+ name: {
3993
4273
  type: 'string',
3994
- description: 'Month key (YYYY-MM)',
3995
- example: '2024-03'
4274
+ description: 'Rule name'
3996
4275
  },
3997
- income: {
4276
+ icon: {
3998
4277
  type: 'string',
3999
- description: 'Income in base currency (absolute, converted)',
4000
- example: '10000.00'
4278
+ description: 'Rule icon'
4001
4279
  },
4002
- expense: {
4280
+ frequency: {
4003
4281
  type: 'string',
4004
- description: 'Expense in base currency (absolute, converted)',
4005
- example: '5000.00'
4282
+ description: 'Rule frequency'
4006
4283
  },
4007
- netSavings: {
4284
+ currency: {
4008
4285
  type: 'string',
4009
- description: 'netSavings = income − expense (savings positive)',
4010
- example: '5000.00'
4286
+ description: 'Currency code'
4011
4287
  }
4012
4288
  },
4013
- required: ['month', 'income', 'expense', 'netSavings']
4289
+ required: ['name', 'frequency', 'currency']
4014
4290
  } as const;
4015
4291
 
4016
- export const $CashFlowTrendSummaryDto = {
4292
+ export const $ExpectedTransactionResponseDto = {
4017
4293
  type: 'object',
4018
4294
  properties: {
4019
- totalIncome: {
4295
+ id: {
4020
4296
  type: 'string',
4021
- description: 'Total income across the period',
4022
- example: '60000.00'
4297
+ description: 'Expected transaction ID'
4023
4298
  },
4024
- totalExpense: {
4299
+ userId: {
4025
4300
  type: 'string',
4026
- description: 'Total expense across the period',
4027
- example: '30000.00'
4301
+ description: 'User ID'
4028
4302
  },
4029
- totalNetSavings: {
4303
+ ruleId: {
4030
4304
  type: 'string',
4031
- description: 'income − expense across the period',
4032
- example: '30000.00'
4305
+ description: 'Associated rule ID'
4033
4306
  },
4034
- averageMonthlyNetSavings: {
4307
+ expectedDate: {
4035
4308
  type: 'string',
4036
- description:
4037
- 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
4038
- example: '5000.00'
4039
- }
4040
- },
4041
- required: [
4042
- 'totalIncome',
4043
- 'totalExpense',
4044
- 'totalNetSavings',
4045
- 'averageMonthlyNetSavings'
4046
- ]
4047
- } as const;
4048
-
4049
- export const $CashFlowTrendsResponseDto = {
4050
- type: 'object',
4051
- properties: {
4052
- series: {
4053
- description:
4054
- 'Monthly cash-flow series (fixed N-month window, zero-filled)',
4055
- type: 'array',
4056
- items: {
4057
- $ref: '#/components/schemas/CashFlowPointDto'
4058
- }
4309
+ description: 'Expected date (YYYY-MM-DD)'
4059
4310
  },
4060
- summary: {
4061
- 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',
4062
4337
  allOf: [
4063
4338
  {
4064
- $ref: '#/components/schemas/CashFlowTrendSummaryDto'
4339
+ $ref: '#/components/schemas/ExpectedTransactionRuleDto'
4065
4340
  }
4066
4341
  ]
4067
4342
  },
4068
- period: {
4069
- type: 'string',
4070
- description: 'Period requested',
4071
- example: '6m'
4072
- },
4073
- granularity: {
4343
+ createdAt: {
4344
+ format: 'date-time',
4074
4345
  type: 'string',
4075
- description: 'Data granularity (v1 returns month buckets)',
4076
- example: 'month'
4346
+ description: 'Created at timestamp'
4077
4347
  },
4078
- currency: {
4348
+ updatedAt: {
4349
+ format: 'date-time',
4079
4350
  type: 'string',
4080
- description: 'Base currency for converted values',
4081
- example: 'CNY'
4082
- },
4083
- warnings: {
4084
- description: 'Exchange rate warnings (e.g. missing rate for a currency)',
4085
- type: 'array',
4086
- items: {
4087
- $ref: '#/components/schemas/ExchangeRateWarningDto'
4088
- }
4351
+ description: 'Updated at timestamp'
4089
4352
  }
4090
4353
  },
4091
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4092
- } as const;
4093
-
4094
- export const $GenerateSnapshotBody = {
4095
- type: 'object',
4096
- properties: {}
4097
- } as const;
4098
-
4099
- export const $GenerateSnapshotResponse = {
4100
- type: 'object',
4101
- properties: {}
4102
- } as const;
4103
-
4104
- export const $BackfillSnapshotsBody = {
4105
- type: 'object',
4106
- properties: {}
4107
- } as const;
4108
-
4109
- export const $BackfillSnapshotsResponse = {
4110
- type: 'object',
4111
- properties: {}
4354
+ required: [
4355
+ 'id',
4356
+ 'userId',
4357
+ 'ruleId',
4358
+ 'expectedDate',
4359
+ 'expectedAmount',
4360
+ 'status',
4361
+ 'isOverdue',
4362
+ 'rule',
4363
+ 'createdAt',
4364
+ 'updatedAt'
4365
+ ]
4112
4366
  } as const;
4113
4367
 
4114
- export const $DeleteOwnUserDto = {
4368
+ export const $ExpectedTransactionListResponseDto = {
4115
4369
  type: 'object',
4116
4370
  properties: {
4117
- accessToken: {
4118
- type: 'string',
4119
- description: 'Access token for user verification',
4120
- 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'
4121
4380
  }
4122
4381
  },
4123
- required: ['accessToken']
4382
+ required: ['items', 'total']
4124
4383
  } as const;
4125
4384
 
4126
- export const $SignupDto = {
4385
+ export const $ConfirmMatchDto = {
4127
4386
  type: 'object',
4128
4387
  properties: {
4129
- turnstileToken: {
4388
+ transactionId: {
4130
4389
  type: 'string',
4131
- description:
4132
- 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4133
- example: '0.abc123def456...'
4390
+ description: 'Transaction ID to match with'
4134
4391
  }
4135
- }
4392
+ },
4393
+ required: ['transactionId']
4136
4394
  } as const;
4137
4395
 
4138
- export const $SignupResponseDto = {
4396
+ export const $EnterNowDto = {
4139
4397
  type: 'object',
4140
4398
  properties: {
4141
- authToken: {
4399
+ expenseAccount: {
4142
4400
  type: 'string',
4143
- description: 'JWT auth token',
4144
- example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
4401
+ description:
4402
+ 'Override expense account (uses rule default if not provided)',
4403
+ maxLength: 200
4145
4404
  },
4146
- accessToken: {
4405
+ paymentAccount: {
4147
4406
  type: 'string',
4148
- 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
4149
4420
  },
4150
- role: {
4421
+ narration: {
4151
4422
  type: 'string',
4152
- description: 'Assigned user role',
4153
- enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
4423
+ description: 'Optional narration',
4424
+ maxLength: 500
4154
4425
  }
4155
- },
4156
- required: ['authToken', 'accessToken', 'role']
4426
+ }
4157
4427
  } as const;
4158
4428
 
4159
- export const $UpdateUserSettingDto = {
4429
+ export const $ForecastItemDto = {
4160
4430
  type: 'object',
4161
4431
  properties: {
4162
- secId: {
4163
- type: 'number',
4164
- description: 'Security ID'
4432
+ rule: {
4433
+ type: 'string',
4434
+ description: 'Rule name',
4435
+ example: 'Rent'
4165
4436
  },
4166
- annualInterestRate: {
4437
+ ruleId: {
4438
+ type: 'string',
4439
+ description: 'Rule ID',
4440
+ example: 'clx123...'
4441
+ },
4442
+ amount: {
4167
4443
  type: 'number',
4168
- description: 'Annual interest rate',
4169
- example: 0.05
4444
+ description: 'Expected amount',
4445
+ example: 3000
4170
4446
  },
4171
- currency: {
4447
+ date: {
4172
4448
  type: 'string',
4173
- description: 'Currency code',
4174
- example: 'USD'
4449
+ description: 'Expected date (YYYY-MM-DD)',
4450
+ example: '2024-04-01'
4175
4451
  },
4176
- baseCurrency: {
4452
+ icon: {
4177
4453
  type: 'string',
4178
- description: 'Base currency code',
4179
- example: 'USD'
4454
+ description: 'Rule icon emoji',
4455
+ example: '🏠',
4456
+ nullable: true
4180
4457
  },
4181
- benchmark: {
4458
+ currency: {
4182
4459
  type: 'string',
4183
- description: 'Benchmark symbol',
4184
- example: 'SPY'
4185
- },
4186
- 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: {
4187
4471
  type: 'string',
4188
- description: 'Color scheme',
4189
- enum: ['DARK', 'LIGHT']
4472
+ description: 'Month (YYYY-MM)',
4473
+ example: '2024-04'
4190
4474
  },
4191
- dateRange: {
4192
- type: 'string',
4193
- description: 'Date range filter',
4194
- example: '1y'
4475
+ expectedOutflow: {
4476
+ type: 'number',
4477
+ description: 'Total expected outflow for the month',
4478
+ example: 8500
4195
4479
  },
4196
- emergencyFund: {
4480
+ itemCount: {
4197
4481
  type: 'number',
4198
- description: 'Emergency fund amount',
4199
- example: 10000
4482
+ description: 'Number of expected transactions',
4483
+ example: 3
4200
4484
  },
4201
- 'filters.accounts': {
4202
- description: 'Account filter IDs',
4203
- type: 'array',
4204
- items: {
4205
- type: 'string'
4485
+ byCurrency: {
4486
+ type: 'object',
4487
+ description: 'Breakdown by currency',
4488
+ example: {
4489
+ CNY: 8500,
4490
+ USD: 100
4206
4491
  }
4207
4492
  },
4208
- 'filters.assetClasses': {
4209
- description: 'Asset class filters',
4493
+ items: {
4494
+ description: 'Individual forecast items',
4210
4495
  type: 'array',
4211
4496
  items: {
4212
- type: 'string'
4497
+ $ref: '#/components/schemas/ForecastItemDto'
4213
4498
  }
4214
- },
4215
- 'filters.dataSource': {
4216
- type: 'string',
4217
- description: 'Data source filter'
4218
- },
4219
- 'filters.symbol': {
4220
- type: 'string',
4221
- description: 'Symbol filter'
4222
- },
4223
- 'filters.tags': {
4224
- 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',
4225
4509
  type: 'array',
4226
4510
  items: {
4227
- type: 'string'
4511
+ $ref: '#/components/schemas/MonthlyForecastDto'
4228
4512
  }
4229
4513
  },
4230
- isExperimentalFeatures: {
4231
- type: 'boolean',
4232
- description: 'Enable experimental features'
4233
- },
4234
- isRestrictedView: {
4235
- type: 'boolean',
4236
- description: 'Enable restricted view mode'
4237
- },
4238
- language: {
4239
- type: 'string',
4240
- description: 'Language code',
4241
- example: 'en'
4242
- },
4243
- locale: {
4244
- type: 'string',
4245
- description: 'Locale code',
4246
- example: 'en-US'
4247
- },
4248
- projectedTotalAmount: {
4514
+ totalOutflow: {
4249
4515
  type: 'number',
4250
- description: 'Projected total amount',
4251
- example: 1000000
4516
+ description: 'Total expected outflow across all months',
4517
+ example: 25500
4252
4518
  },
4253
- retirementDate: {
4254
- type: 'string',
4255
- description: 'Retirement date in ISO 8601 format',
4256
- 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
+ }
4257
4526
  },
4258
- savingsRate: {
4527
+ rulesCount: {
4259
4528
  type: 'number',
4260
- description: 'Savings rate percentage',
4261
- example: 0.2
4529
+ description: 'Number of active recurring rules included',
4530
+ example: 5
4262
4531
  },
4263
- viewMode: {
4532
+ periodStart: {
4264
4533
  type: 'string',
4265
- description: 'View mode',
4266
- enum: ['DEFAULT', 'ZEN']
4267
- }
4268
- }
4269
- } as const;
4270
-
4271
- export const $UpdatePropertyDto = {
4272
- type: 'object',
4273
- properties: {
4274
- value: {
4534
+ description: 'Forecast period start date',
4535
+ example: '2024-04-01'
4536
+ },
4537
+ periodEnd: {
4275
4538
  type: 'string',
4276
- description: 'Property value'
4539
+ description: 'Forecast period end date',
4540
+ example: '2024-06-30'
4277
4541
  }
4278
4542
  },
4279
- required: ['value']
4543
+ required: [
4544
+ 'forecast',
4545
+ 'totalOutflow',
4546
+ 'totalByCurrency',
4547
+ 'rulesCount',
4548
+ 'periodStart',
4549
+ 'periodEnd'
4550
+ ]
4280
4551
  } as const;
4281
4552
 
4282
4553
  export const $CreateTransactionRuleDto = {
@@ -4838,7 +5109,8 @@ export const $UpdateTransactionRuleDto = {
4838
5109
  },
4839
5110
  matchLogic: {
4840
5111
  type: 'string',
4841
- enum: ['OR', 'AND']
5112
+ enum: ['OR', 'AND'],
5113
+ default: 'OR'
4842
5114
  },
4843
5115
  amountMin: {
4844
5116
  type: 'number',
@@ -4852,13 +5124,10 @@ export const $UpdateTransactionRuleDto = {
4852
5124
  },
4853
5125
  priority: {
4854
5126
  type: 'number',
5127
+ default: 50,
4855
5128
  minimum: 0,
4856
5129
  maximum: 1000
4857
5130
  },
4858
- enabled: {
4859
- type: 'boolean',
4860
- description: 'Enable or disable the rule'
4861
- },
4862
5131
  additionalTags: {
4863
5132
  items: {
4864
5133
  type: 'array'
@@ -4868,6 +5137,10 @@ export const $UpdateTransactionRuleDto = {
4868
5137
  },
4869
5138
  additionalMetadata: {
4870
5139
  type: 'object'
5140
+ },
5141
+ enabled: {
5142
+ type: 'boolean',
5143
+ description: 'Enable or disable the rule'
4871
5144
  }
4872
5145
  }
4873
5146
  } as const;
@@ -4896,36 +5169,113 @@ export const $TestRuleDto = {
4896
5169
  maxLength: 10
4897
5170
  }
4898
5171
  },
4899
- 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']
4900
5254
  } as const;
4901
5255
 
4902
- export const $TestRuleResponseDto = {
5256
+ export const $CategoryCatalogListResponseDto = {
4903
5257
  type: 'object',
4904
5258
  properties: {
4905
- ruleId: {
4906
- type: 'string',
4907
- description: 'Rule ID that was tested'
4908
- },
4909
- matches: {
4910
- type: 'boolean',
4911
- 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
+ }
4912
5265
  },
4913
- confidence: {
5266
+ total: {
4914
5267
  type: 'number',
4915
- description: 'Match confidence score (0-1)',
4916
- example: 0.85
5268
+ description:
5269
+ 'Total category entries for the region (before query filtering)',
5270
+ example: 30
4917
5271
  },
4918
- matchDetails: {
4919
- type: 'object',
4920
- description: 'Details of which fields matched',
4921
- example: {
4922
- narration: true,
4923
- payee: false,
4924
- categoryAccount: false
4925
- }
5272
+ region: {
5273
+ type: 'string',
5274
+ description: 'Region code',
5275
+ example: 'cn'
4926
5276
  }
4927
5277
  },
4928
- required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5278
+ required: ['items', 'total', 'region']
4929
5279
  } as const;
4930
5280
 
4931
5281
  export const $CreateBeanEventDto = {
@@ -5091,6 +5441,14 @@ export const $OnboardingAccountDto = {
5091
5441
  description:
5092
5442
  'Platform ID to bind the account to (references Platform.id); omit for unbound',
5093
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'
5094
5452
  }
5095
5453
  },
5096
5454
  required: ['path', 'currency']
@@ -5670,7 +6028,8 @@ export const $UpdateMapperDefaultsDto = {
5670
6028
  type: 'string',
5671
6029
  description: 'Source account for transactions (Beancount format)',
5672
6030
  example: 'Assets:CN:Alipay:Balance',
5673
- 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-]*)+$'
5674
6033
  },
5675
6034
  currency: {
5676
6035
  type: 'string',
@@ -5684,13 +6043,15 @@ export const $UpdateMapperDefaultsDto = {
5684
6043
  type: 'string',
5685
6044
  description: 'Default expense account (optional)',
5686
6045
  example: 'Expenses:Unknown',
5687
- 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-]*)+$'
5688
6048
  },
5689
6049
  incomeAccount: {
5690
6050
  type: 'string',
5691
6051
  description: 'Default income account (optional)',
5692
6052
  example: 'Income:Unknown',
5693
- 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-]*)+$'
5694
6055
  },
5695
6056
  methodAccountMapping: {
5696
6057
  type: 'object',
@@ -5979,15 +6340,106 @@ export const $UncoveredFormatMissDto = {
5979
6340
  properties: {}
5980
6341
  } as const;
5981
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
+
5982
6427
  export const $ProcessNlpDto = {
5983
6428
  type: 'object',
5984
6429
  properties: {
5985
6430
  message: {
5986
6431
  type: 'string',
5987
- description: 'Natural language text describing a transaction (Chinese)',
5988
- 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',
5989
6435
  maxLength: 500
5990
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
+ },
5991
6443
  sessionId: {
5992
6444
  type: 'string',
5993
6445
  description:
@@ -5995,14 +6447,18 @@ export const $ProcessNlpDto = {
5995
6447
  example: 'session_abc123'
5996
6448
  },
5997
6449
  parsedData: {
5998
- type: 'object',
5999
6450
  description:
6000
6451
  'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
6001
6452
  example: {
6002
6453
  amount: 35,
6003
6454
  currency: 'CNY',
6004
6455
  payee: 'Starbucks'
6005
- }
6456
+ },
6457
+ allOf: [
6458
+ {
6459
+ $ref: '#/components/schemas/ClientParsedDataDto'
6460
+ }
6461
+ ]
6006
6462
  },
6007
6463
  selectedRuleId: {
6008
6464
  type: 'string',
@@ -6013,11 +6469,29 @@ export const $ProcessNlpDto = {
6013
6469
  selectedAccount: {
6014
6470
  type: 'string',
6015
6471
  description:
6016
- '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.',
6017
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'
6018
6493
  }
6019
- },
6020
- required: ['message']
6494
+ }
6021
6495
  } as const;
6022
6496
 
6023
6497
  export const $NlpTransactionInfoDto = {
@@ -6083,7 +6557,9 @@ export const $NlpParsedDataDto = {
6083
6557
  },
6084
6558
  category: {
6085
6559
  type: 'string',
6086
- 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'
6087
6563
  },
6088
6564
  incomeType: {
6089
6565
  type: 'string',
@@ -6329,6 +6805,24 @@ export const $NlpRuleConfirmationDataDto = {
6329
6805
  ]
6330
6806
  } as const;
6331
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
+
6332
6826
  export const $NlpAccountConfirmationDataDto = {
6333
6827
  type: 'object',
6334
6828
  properties: {
@@ -6344,10 +6838,11 @@ export const $NlpAccountConfirmationDataDto = {
6344
6838
  example: 'Expenses:Food:Drinks'
6345
6839
  },
6346
6840
  similarAccounts: {
6347
- description: 'Similar accounts for user selection',
6841
+ description:
6842
+ 'Similar accounts for user selection (path + localized name, #680)',
6348
6843
  type: 'array',
6349
6844
  items: {
6350
- type: 'string'
6845
+ $ref: '#/components/schemas/NlpAccountCandidateDto'
6351
6846
  }
6352
6847
  },
6353
6848
  errorMessage: {
@@ -6635,7 +7130,7 @@ export const $NlpResponseDto = {
6635
7130
  type: 'string',
6636
7131
  description:
6637
7132
  'Asset sub-type (only present when intent is "asset"). Determines which asset-related form to render.',
6638
- enum: ['transfer', 'banking', 'investment'],
7133
+ enum: ['transfer', 'banking', 'investment', 'lend', 'lend_collect'],
6639
7134
  example: 'investment'
6640
7135
  },
6641
7136
  liabilitySubType: {
@@ -6838,13 +7333,26 @@ export const $PlatformListItemDto = {
6838
7333
  suggestedSegment: {
6839
7334
  type: 'string',
6840
7335
  description:
6841
- '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")'
6842
7337
  },
6843
7338
  logoUrl: {
6844
7339
  type: 'string',
6845
7340
  description: 'Logo URL',
6846
7341
  nullable: true
6847
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
+ },
6848
7356
  isBound: {
6849
7357
  type: 'boolean',
6850
7358
  description: 'Whether user has accounts using this platform'
@@ -6858,6 +7366,8 @@ export const $PlatformListItemDto = {
6858
7366
  'canonical',
6859
7367
  'suggestedSegment',
6860
7368
  'logoUrl',
7369
+ 'countryCode',
7370
+ 'category',
6861
7371
  'isBound'
6862
7372
  ]
6863
7373
  } as const;
@@ -6875,7 +7385,110 @@ export const $PlatformMatchResultDto = {
6875
7385
  },
6876
7386
  canonical: {
6877
7387
  type: 'string',
6878
- description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7388
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7389
+ },
7390
+ type: {
7391
+ type: 'string',
7392
+ description: 'Platform type',
7393
+ enum: [
7394
+ 'BANK',
7395
+ 'BROKERAGE',
7396
+ 'CRYPTO_EXCHANGE',
7397
+ 'PAYMENT',
7398
+ 'INVESTMENT',
7399
+ 'INSURANCE',
7400
+ 'OTHER'
7401
+ ]
7402
+ },
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")'
6879
7492
  },
6880
7493
  type: {
6881
7494
  type: 'string',
@@ -6890,59 +7503,44 @@ export const $PlatformMatchResultDto = {
6890
7503
  'OTHER'
6891
7504
  ]
6892
7505
  },
6893
- suggestedSegment: {
7506
+ category: {
6894
7507
  type: 'string',
6895
7508
  description:
6896
- 'Suggested path segment — canonical, already in ACCOUNT_RE format'
6897
- },
6898
- logoUrl: {
6899
- type: 'string',
6900
- description: 'Logo URL',
6901
- nullable: true
6902
- },
6903
- matchType: {
6904
- type: 'string',
6905
- description: "How this row matched: 'exact' > 'prefix' > 'substring'",
6906
- 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'
6907
7512
  }
6908
7513
  },
6909
- required: [
6910
- 'id',
6911
- 'name',
6912
- 'canonical',
6913
- 'type',
6914
- 'suggestedSegment',
6915
- 'logoUrl',
6916
- 'matchType'
6917
- ]
7514
+ required: ['id', 'name', 'canonical', 'suggestedSegment', 'type', 'category']
6918
7515
  } as const;
6919
7516
 
6920
- export const $PlatformMatchResponseDto = {
7517
+ export const $PlatformStandardsResponseDto = {
6921
7518
  type: 'object',
6922
7519
  properties: {
6923
- platforms: {
6924
- description: 'Ranked matches, best tier first (at most 10 rows)',
6925
- type: 'array',
6926
- items: {
6927
- $ref: '#/components/schemas/PlatformMatchResultDto'
6928
- }
7520
+ platform: {
7521
+ description: 'The selected platform (institution lock source)',
7522
+ allOf: [
7523
+ {
7524
+ $ref: '#/components/schemas/PlatformStandardsPlatformDto'
7525
+ }
7526
+ ]
6929
7527
  },
6930
- matchType: {
7528
+ region: {
6931
7529
  type: 'string',
6932
7530
  description:
6933
- "Overall match quality — top row's tier, or 'none' when no hits",
6934
- enum: ['none', 'exact', 'prefix', 'substring']
6935
- },
6936
- total: {
6937
- type: 'number',
6938
- 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'
6939
7533
  },
6940
- hasMore: {
6941
- type: 'boolean',
6942
- 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
+ }
6943
7541
  }
6944
7542
  },
6945
- required: ['platforms', 'matchType', 'total', 'hasMore']
7543
+ required: ['platform', 'region', 'templates']
6946
7544
  } as const;
6947
7545
 
6948
7546
  export const $CreatePlatformDto = {
@@ -7046,7 +7644,8 @@ export const $UpdatePlatformDto = {
7046
7644
  },
7047
7645
  isActive: {
7048
7646
  type: 'boolean',
7049
- description: 'Whether the platform is active'
7647
+ description: 'Whether the platform is active',
7648
+ default: true
7050
7649
  }
7051
7650
  }
7052
7651
  } as const;
@@ -7209,7 +7808,8 @@ export const $AccountItemDto = {
7209
7808
  },
7210
7809
  displayName: {
7211
7810
  type: 'string',
7212
- 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)',
7213
7813
  example: 'Savings'
7214
7814
  },
7215
7815
  balance: {
@@ -7245,7 +7845,8 @@ export const $PlatformGroupDto = {
7245
7845
  example: 'CMB Bank'
7246
7846
  },
7247
7847
  accounts: {
7248
- description: 'Accounts within this platform',
7848
+ description:
7849
+ 'Accounts within this platform (Assets and Liabilities rows, #696)',
7249
7850
  type: 'array',
7250
7851
  items: {
7251
7852
  $ref: '#/components/schemas/AccountItemDto'
@@ -7253,7 +7854,8 @@ export const $PlatformGroupDto = {
7253
7854
  },
7254
7855
  totalBalance: {
7255
7856
  type: 'string',
7256
- 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)',
7257
7859
  example: '100000.00'
7258
7860
  },
7259
7861
  balanceByCurrency: {
@@ -7272,7 +7874,7 @@ export const $PlatformGroupDto = {
7272
7874
  sharePct: {
7273
7875
  type: 'number',
7274
7876
  description:
7275
- '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)',
7276
7878
  example: 42.5
7277
7879
  }
7278
7880
  },
@@ -7320,7 +7922,8 @@ export const $AccountsSummaryDto = {
7320
7922
  properties: {
7321
7923
  totalAccounts: {
7322
7924
  type: 'number',
7323
- description: 'Total number of accounts'
7925
+ description:
7926
+ 'Total number of accounts (balance sheet: Assets + Liabilities, #696)'
7324
7927
  },
7325
7928
  totalPlatforms: {
7326
7929
  type: 'number',
@@ -7378,7 +7981,8 @@ export const $AccountItemWithAssetClassDto = {
7378
7981
  },
7379
7982
  displayName: {
7380
7983
  type: 'string',
7381
- 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)',
7382
7986
  example: 'Savings'
7383
7987
  },
7384
7988
  balance: {
@@ -7864,7 +8468,7 @@ export const $MonetaryDto = {
7864
8468
  example: 'USD'
7865
8469
  },
7866
8470
  baseCcyEquivalent: {
7867
- type: 'object',
8471
+ type: 'string',
7868
8472
  description: 'Converted to user base currency (Decimal string)',
7869
8473
  example: '21600',
7870
8474
  nullable: true
@@ -7940,13 +8544,13 @@ export const $HoldingPnlRowDto = {
7940
8544
  example: 'Assets:US:Broker:AAPL'
7941
8545
  },
7942
8546
  accountCcy: {
7943
- type: 'object',
8547
+ type: 'string',
7944
8548
  description: 'Account settlement currency (ISO 4217), from cost currency',
7945
8549
  nullable: true,
7946
8550
  example: 'USD'
7947
8551
  },
7948
8552
  brokerType: {
7949
- type: 'object',
8553
+ type: 'string',
7950
8554
  description: 'Broker type derived from Platform.type',
7951
8555
  nullable: true,
7952
8556
  example: 'broker'
@@ -7967,7 +8571,7 @@ export const $HoldingPnlRowDto = {
7967
8571
  example: 'EQUITY'
7968
8572
  },
7969
8573
  assetSubClass: {
7970
- type: 'object',
8574
+ type: 'string',
7971
8575
  nullable: true,
7972
8576
  example: 'STOCK'
7973
8577
  },
@@ -8014,14 +8618,14 @@ export const $HoldingPnlRowDto = {
8014
8618
  ]
8015
8619
  },
8016
8620
  unrealizedPnlBase: {
8017
- type: 'object',
8621
+ type: 'string',
8018
8622
  description:
8019
8623
  'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
8020
8624
  nullable: true,
8021
8625
  example: '6000'
8022
8626
  },
8023
8627
  unrealizedPnlPct: {
8024
- type: 'object',
8628
+ type: 'string',
8025
8629
  description: 'Unrealized P&L % (Decimal string)',
8026
8630
  nullable: true,
8027
8631
  example: '25'
@@ -8045,7 +8649,7 @@ export const $HoldingPnlRowDto = {
8045
8649
  ]
8046
8650
  },
8047
8651
  pctOfInvestedAssets: {
8048
- type: 'object',
8652
+ type: 'string',
8049
8653
  description:
8050
8654
  'Share of invested assets % (Decimal string); only for invested chartTokens',
8051
8655
  nullable: true,
@@ -8090,15 +8694,15 @@ export const $HoldingPnlWarningDto = {
8090
8694
  ]
8091
8695
  },
8092
8696
  symbol: {
8093
- type: 'object',
8697
+ type: 'string',
8094
8698
  nullable: true
8095
8699
  },
8096
8700
  accountId: {
8097
- type: 'object',
8701
+ type: 'string',
8098
8702
  nullable: true
8099
8703
  },
8100
8704
  currency: {
8101
- type: 'object',
8705
+ type: 'string',
8102
8706
  nullable: true
8103
8707
  }
8104
8708
  },
@@ -8162,6 +8766,216 @@ export const $AnonymousLoginResponseDto = {
8162
8766
  required: ['authToken']
8163
8767
  } as const;
8164
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
+
8165
8979
  export const $SymbolSearchResultDto = {
8166
8980
  type: 'object',
8167
8981
  properties: {
@@ -8170,35 +8984,35 @@ export const $SymbolSearchResultDto = {
8170
8984
  example: 'AAPL'
8171
8985
  },
8172
8986
  name: {
8173
- type: 'object',
8987
+ type: 'string',
8174
8988
  example: 'Apple Inc.',
8175
8989
  nullable: true
8176
8990
  },
8177
8991
  exchange: {
8178
- type: 'object',
8992
+ type: 'string',
8179
8993
  example: 'US',
8180
8994
  nullable: true
8181
8995
  },
8182
8996
  assetType: {
8183
- type: 'object',
8997
+ type: 'string',
8184
8998
  description: 'OpenBB asset_type (e.g. stock, etf)',
8185
8999
  example: 'stock',
8186
9000
  nullable: true
8187
9001
  },
8188
9002
  assetClass: {
8189
- type: 'object',
9003
+ type: 'string',
8190
9004
  description: 'IGN asset class (region.types.ts ASSET_CLASSES)',
8191
9005
  example: 'EQUITY',
8192
9006
  nullable: true
8193
9007
  },
8194
9008
  assetSubClass: {
8195
- type: 'object',
9009
+ type: 'string',
8196
9010
  description: 'IGN asset sub-class (region.types.ts ASSET_SUB_CLASSES)',
8197
9011
  example: 'STOCK',
8198
9012
  nullable: true
8199
9013
  },
8200
9014
  currency: {
8201
- type: 'object',
9015
+ type: 'string',
8202
9016
  description: 'Trading currency (extra_data or inferred from exchange)',
8203
9017
  example: 'USD',
8204
9018
  nullable: true
@@ -8215,90 +9029,90 @@ export const $SymbolQuoteDto = {
8215
9029
  example: 'AAPL'
8216
9030
  },
8217
9031
  name: {
8218
- type: 'object',
9032
+ type: 'string',
8219
9033
  example: 'Apple Inc.',
8220
9034
  nullable: true
8221
9035
  },
8222
9036
  exchange: {
8223
- type: 'object',
9037
+ type: 'string',
8224
9038
  example: 'US',
8225
9039
  nullable: true
8226
9040
  },
8227
9041
  assetType: {
8228
- type: 'object',
9042
+ type: 'string',
8229
9043
  description: 'OpenBB asset_type',
8230
9044
  example: 'stock',
8231
9045
  nullable: true
8232
9046
  },
8233
9047
  assetClass: {
8234
- type: 'object',
9048
+ type: 'string',
8235
9049
  description: 'IGN asset class',
8236
9050
  example: 'EQUITY',
8237
9051
  nullable: true
8238
9052
  },
8239
9053
  assetSubClass: {
8240
- type: 'object',
9054
+ type: 'string',
8241
9055
  description: 'IGN asset sub-class',
8242
9056
  example: 'STOCK',
8243
9057
  nullable: true
8244
9058
  },
8245
9059
  currency: {
8246
- type: 'object',
9060
+ type: 'string',
8247
9061
  description: 'Trading currency (extra_data or inferred from exchange)',
8248
9062
  example: 'USD',
8249
9063
  nullable: true
8250
9064
  },
8251
9065
  price: {
8252
- type: 'object',
9066
+ type: 'string',
8253
9067
  description: 'Latest price (Decimal string)',
8254
9068
  example: '189.84',
8255
9069
  nullable: true
8256
9070
  },
8257
9071
  priceDate: {
8258
- type: 'object',
9072
+ type: 'string',
8259
9073
  description: 'Date the price was observed (ISO yyyy-MM-dd)',
8260
9074
  example: '2026-08-05',
8261
9075
  nullable: true
8262
9076
  },
8263
9077
  changePercent: {
8264
- type: 'object',
9078
+ type: 'number',
8265
9079
  description:
8266
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.',
8267
9081
  example: 1.7,
8268
9082
  nullable: true
8269
9083
  },
8270
9084
  prevClose: {
8271
- type: 'object',
9085
+ type: 'string',
8272
9086
  description: 'Previous close (Decimal string)',
8273
9087
  nullable: true
8274
9088
  },
8275
9089
  open: {
8276
- type: 'object',
9090
+ type: 'string',
8277
9091
  description: 'Day open (Decimal string)',
8278
9092
  nullable: true
8279
9093
  },
8280
9094
  high: {
8281
- type: 'object',
9095
+ type: 'string',
8282
9096
  description: 'Day high (Decimal string)',
8283
9097
  nullable: true
8284
9098
  },
8285
9099
  low: {
8286
- type: 'object',
9100
+ type: 'string',
8287
9101
  description: 'Day low (Decimal string)',
8288
9102
  nullable: true
8289
9103
  },
8290
9104
  volume: {
8291
- type: 'object',
9105
+ type: 'string',
8292
9106
  description: 'Day volume (Decimal string)',
8293
9107
  nullable: true
8294
9108
  },
8295
9109
  yearHigh: {
8296
- type: 'object',
9110
+ type: 'string',
8297
9111
  description: '52-week high (Decimal string)',
8298
9112
  nullable: true
8299
9113
  },
8300
9114
  yearLow: {
8301
- type: 'object',
9115
+ type: 'string',
8302
9116
  description: '52-week low (Decimal string)',
8303
9117
  nullable: true
8304
9118
  }