@firela/api-types 0.0.0-canary.97006feb → 0.0.0-canary.9d18b250

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